difftreelog
refactor! decouple pallet-collator-selection from pallet-configuration (#962)
in: master
* feat!: decoupling configuration&collators pallets BREAKING CHANGE: pallet `collator-selection` no longer has a tight coupling with the `configuration` pallet * refactor(collator-selection): type bounds, tests * refactor(collator-selection): tests * add ci step for banchmark tests * add opsFee tests ---------
15 files changed
.github/workflows/yarn-dev.ymldiffbeforeafterboth--- a/.github/workflows/yarn-dev.yml
+++ b/.github/workflows/yarn-dev.yml
@@ -87,6 +87,18 @@
run: |
echo "url is ${{ steps.test-report.outputs.runHtmlUrl }}"
+ - name: Run benchmark mintFee tests
+ working-directory: tests
+ run: |
+ yarn install
+ npx ts-node --esm ./src/benchmarks/mintFee/index.ts
+
+ - name: Run benchmark opsFee tests
+ working-directory: tests
+ run: |
+ yarn install
+ npx ts-node --esm ./src/benchmarks/opsFee/index.ts
+
- name: Stop running containers
if: always() # run this step always
run: docker-compose -f ".docker/docker-compose.${{ matrix.network }}.yml" down
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6371,7 +6371,7 @@
[[package]]
name = "pallet-collator-selection"
-version = "4.0.0"
+version = "5.0.0"
dependencies = [
"frame-benchmarking",
"frame-support",
@@ -6380,7 +6380,6 @@
"pallet-aura",
"pallet-authorship",
"pallet-balances",
- "pallet-configuration",
"pallet-session",
"pallet-timestamp",
"parity-scale-codec",
@@ -6435,7 +6434,7 @@
[[package]]
name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
dependencies = [
"fp-evm",
"frame-benchmarking",
pallets/collator-selection/CHANGELOG.mddiffbeforeafterboth--- a/pallets/collator-selection/CHANGELOG.md
+++ b/pallets/collator-selection/CHANGELOG.md
@@ -4,22 +4,28 @@
<!-- bureaucracy goes here -->
+## [5.0.0] - 2023-06-27
+
+### Major change
+
+- The dependency (tight coupling) this pallet on the configuration pallet has been removed.
+
## [4.0.0] - 2022-12-29
### Added
- Entire functionality moved over from cumulus/pallet-collator-selection (v3.0.0). Refactored business logic:
- - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited.
- - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
- - Active candidates may `offboard`, but they will retain their license.
- - Deposit is returned, and candidacy possibly removed with `release_license`.
- - License can be forcibly forfeited and candidacy removed with `force_release_license`.
- - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
- - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
- - Remove `MinCandidates`.
- - Minimal amount of invulnerables is now 1.
- - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
- - `KickThreshold` is made configurable.
- - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
- - Naming changes to better reflect the new functionality.
- - More minor changes, tests, benchmarks, etc.
\ No newline at end of file
+ - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited.
+ - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
+ - Active candidates may `offboard`, but they will retain their license.
+ - Deposit is returned, and candidacy possibly removed with `release_license`.
+ - License can be forcibly forfeited and candidacy removed with `force_release_license`.
+ - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
+ - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
+ - Remove `MinCandidates`.
+ - Minimal amount of invulnerables is now 1.
+ - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
+ - `KickThreshold` is made configurable.
+ - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
+ - Naming changes to better reflect the new functionality.
+ - More minor changes, tests, benchmarks, etc.
pallets/collator-selection/Cargo.tomldiffbeforeafterboth--- a/pallets/collator-selection/Cargo.toml
+++ b/pallets/collator-selection/Cargo.toml
@@ -6,7 +6,7 @@
license = "GPLv3"
name = "pallet-collator-selection"
repository = "https://github.com/UniqueNetwork/unique-chain"
-version = "4.0.0"
+version = "5.0.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
@@ -23,7 +23,6 @@
frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-authorship = { workspace = true }
-pallet-configuration = { workspace = true }
pallet-session = { workspace = true }
sp-runtime = { workspace = true }
sp-staking = { workspace = true }
@@ -55,8 +54,6 @@
"frame-system/std",
"log/std",
"pallet-authorship/std",
- 'pallet-aura/std',
- 'pallet-balances/std',
"pallet-session/std",
"rand/std",
"scale-info/std",
@@ -64,6 +61,8 @@
"sp-runtime/std",
"sp-staking/std",
"sp-std/std",
+ 'pallet-aura/std',
+ 'pallet-balances/std',
]
try-runtime = ["frame-support/try-runtime"]
pallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/benchmarking.rs
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -35,7 +35,7 @@
use super::*;
#[allow(unused)]
-use crate::Pallet as CollatorSelection;
+use crate::{Pallet as CollatorSelection, BalanceOf};
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
use frame_support::{
assert_ok,
@@ -49,11 +49,6 @@
use frame_system::{EventRecord, RawOrigin};
use pallet_authorship::EventHandler;
use pallet_session::{self as session, SessionManager};
-use pallet_configuration::{
- self as configuration, BalanceOf,
- CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
- CollatorSelectionLicenseBondOverride as LicenseBond,
-};
use sp_std::prelude::*;
const SEED: u32 = 0;
@@ -117,7 +112,7 @@
validators.into_iter().map(|(who, _)| who).collect()
}
-fn register_invulnerables<T: Config + configuration::Config>(count: u32) {
+fn register_invulnerables<T: Config>(count: u32) {
let candidates = (0..count)
.map(|c| account("candidate", c, SEED))
.collect::<Vec<_>>();
@@ -131,33 +126,27 @@
}
}
-fn register_candidates<T: Config + configuration::Config>(count: u32) {
+fn register_candidates<T: Config>(count: u32) {
let candidates = (0..count)
.map(|c| account("candidate", c, SEED))
.collect::<Vec<_>>();
- assert!(
- <LicenseBond<T>>::get() > 0u32.into(),
- "Bond cannot be zero!"
- );
+ assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
for who in candidates {
- T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+ T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
<CollatorSelection<T>>::onboard(RawOrigin::Signed(who).into()).unwrap();
}
}
-fn get_licenses<T: Config + configuration::Config>(count: u32) {
+fn get_licenses<T: Config>(count: u32) {
let candidates = (0..count)
.map(|c| account("candidate", c, SEED))
.collect::<Vec<_>>();
- assert!(
- <LicenseBond<T>>::get() > 0u32.into(),
- "Bond cannot be zero!"
- );
+ assert!(T::LicenseBond::get() > 0u32.into(), "Bond cannot be zero!");
for who in candidates {
- T::Currency::set_balance(&who, <LicenseBond<T>>::get() * 2u32.into());
+ T::Currency::set_balance(&who, T::LicenseBond::get() * 2u32.into());
<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
}
}
@@ -165,7 +154,7 @@
/// `Currency::minimum_balance` was used originally, but in unique-chain, we have
/// zero existential deposit, thus triggering zero bond assertion.
fn balance_unit<T: Config>() -> BalanceOf<T> {
- 200u32.into()
+ T::LicenseBond::get()
}
/// Our benchmarking environment already has invulnerables registered.
@@ -173,7 +162,7 @@
benchmarks! {
where_clause { where
- T: pallet_authorship::Config + session::Config + configuration::Config
+ T: Config + pallet_authorship::Config + session::Config
}
// todo:collator this and all the following do not work for some reason, going all the way up to 10 in length
@@ -188,7 +177,7 @@
let new_invulnerable: T::AccountId = whitelisted_caller();
let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
- T::Currency::set_balance(&new_invulnerable, bond.clone());
+ <T as Config>::Currency::set_balance(&new_invulnerable, bond);
<session::Pallet<T>>::set_keys(
RawOrigin::Signed(new_invulnerable.clone()).into(),
@@ -225,15 +214,13 @@
get_license {
let c in 1 .. T::MaxCollators::get() - 1;
-
- <LicenseBond<T>>::put(balance_unit::<T>());
register_validators::<T>(c);
get_licenses::<T>(c);
let caller: T::AccountId = whitelisted_caller();
let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
- T::Currency::set_balance(&caller, bond.clone());
+ T::Currency::set_balance(&caller, bond);
<session::Pallet<T>>::set_keys(
RawOrigin::Signed(caller.clone()).into(),
@@ -251,15 +238,12 @@
onboard {
let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES - 1;
- <LicenseBond<T>>::put(balance_unit::<T>());
- <DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES + 1);
-
register_validators::<T>(c);
register_candidates::<T>(c);
let caller: T::AccountId = whitelisted_caller();
let bond: BalanceOf<T> = balance_unit::<T>() * 2u32.into();
- T::Currency::set_balance(&caller, bond.clone());
+ T::Currency::set_balance(&caller, bond);
let origin = RawOrigin::Signed(caller.clone());
@@ -279,9 +263,7 @@
// worst case is the last candidate leaving.
offboard {
- let c in 1 .. T::MaxCollators::get();
- <LicenseBond<T>>::put(balance_unit::<T>());
- <DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
+ let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
register_validators::<T>(c);
register_candidates::<T>(c);
@@ -295,10 +277,8 @@
// worst case is the last candidate leaving.
release_license {
- let c in 1 .. T::MaxCollators::get();
+ let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
let bond = balance_unit::<T>();
- <LicenseBond<T>>::put(bond);
- <DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
register_validators::<T>(c);
register_candidates::<T>(c);
@@ -312,10 +292,8 @@
// worst case is the last candidate leaving.
force_release_license {
- let c in 1 .. T::MaxCollators::get();
+ let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
let bond = balance_unit::<T>();
- <LicenseBond<T>>::put(bond);
- <DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
register_validators::<T>(c);
register_candidates::<T>(c);
@@ -334,7 +312,6 @@
// worst case is paying a non-existing candidate account.
note_author {
- <LicenseBond<T>>::put(balance_unit::<T>());
T::Currency::set_balance(
&<CollatorSelection<T>>::account_id(),
balance_unit::<T>() * 4u32.into(),
@@ -353,11 +330,9 @@
// worst case for new session.
new_session {
- let r in 1 .. T::MaxCollators::get();
- let c in 1 .. T::MaxCollators::get();
+ let r in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
+ let c in 1 .. T::MaxCollators::get() - INITIAL_INVULNERABLES;
- <LicenseBond<T>>::put(balance_unit::<T>());
- <DesiredCollators<T>>::put(c + INITIAL_INVULNERABLES);
frame_system::Pallet::<T>::set_block_number(0u32.into());
register_validators::<T>(c);
pallets/collator-selection/src/lib.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/lib.rs
+++ b/pallets/collator-selection/src/lib.rs
@@ -90,6 +90,10 @@
mod benchmarking;
pub mod weights;
+use frame_support::traits::fungible::Inspect;
+
+type BalanceOf<T> =
+ <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
#[frame_support::pallet]
pub mod pallet {
use super::*;
@@ -111,11 +115,6 @@
use frame_system::pallet_prelude::*;
use pallet_session::SessionManager;
use sp_runtime::{Perbill, traits::Convert};
- use pallet_configuration::{
- CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
- CollatorSelectionLicenseBondOverride as LicenseBond,
- CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,
- };
use sp_staking::SessionIndex;
/// A convertor from collators id. Since this pallet does not have stash/controller, this is
@@ -129,9 +128,12 @@
/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
- pub trait Config: frame_system::Config + pallet_configuration::Config {
+ pub trait Config: frame_system::Config {
/// Overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+ type Currency: Mutate<Self::AccountId>
+ + MutateHold<Self::AccountId>
+ + BalancedHold<Self::AccountId>;
/// Origin that can dictate updating parameters of this pallet.
type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
@@ -163,7 +165,13 @@
type WeightInfo: WeightInfo;
#[pallet::constant]
- type LicenceBondIdentifier: Get<<<Self as pallet_configuration::Config>::Currency as InspectHold<Self::AccountId>>::Reason>;
+ type LicenceBondIdentifier: Get<<Self::Currency as InspectHold<Self::AccountId>>::Reason>;
+
+ type DesiredCollators: Get<u32>;
+
+ type LicenseBond: Get<BalanceOf<Self>>;
+
+ type KickThreshold: Get<Self::BlockNumber>;
}
#[pallet::pallet]
@@ -365,7 +373,7 @@
Error::<T>::ValidatorNotRegistered
);
- let deposit = <LicenseBond<T>>::get();
+ let deposit = T::LicenseBond::get();
T::Currency::hold(&T::LicenceBondIdentifier::get(), &who, deposit)?;
LicenseDepositOf::<T>::insert(who.clone(), deposit);
@@ -396,7 +404,7 @@
let length = <Candidates<T>>::decode_len().unwrap_or_default()
+ <Invulnerables<T>>::decode_len().unwrap_or_default();
ensure!(
- (length as u32) < <DesiredCollators<T>>::get(),
+ (length as u32) < T::DesiredCollators::get(),
Error::<T>::TooManyCandidates
);
ensure!(
@@ -415,7 +423,7 @@
// First authored block is current block plus kick threshold to handle session delay
<LastAuthoredBlock<T>>::insert(
who.clone(),
- frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),
+ frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
);
Ok(candidates.len())
}
@@ -574,7 +582,7 @@
candidates: BoundedVec<T::AccountId, T::MaxCollators>,
) -> BoundedVec<T::AccountId, T::MaxCollators> {
let now = frame_system::Pallet::<T>::block_number();
- let kick_threshold = <KickThreshold<T>>::get();
+ let kick_threshold = T::KickThreshold::get();
candidates
.into_iter()
.filter_map(|c| {
pallets/collator-selection/src/mock.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -63,7 +63,6 @@
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},
Authorship: pallet_authorship::{Pallet, Storage},
- Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>},
}
);
@@ -216,21 +215,6 @@
pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
pub const DayRelayBlocks: u32 = 1;
pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
-}
-
-impl pallet_configuration::Config for Test {
- type RuntimeEvent = RuntimeEvent;
- type Currency = Balances;
- type DefaultCollatorSelectionMaxCollators = MaxCollators;
- type DefaultCollatorSelectionKickThreshold = KickThreshold;
- type DefaultCollatorSelectionLicenseBond = LicenseBond;
- // the following constants we don't care about
- type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;
- type DefaultMinGasPrice = DefaultMinGasPrice;
- type MaxXcmAllowedLocations = MaxXcmAllowedLocations;
- type AppPromotionDailyRate = AppPromotionDailyRate;
- type DayRelayBlocks = DayRelayBlocks;
- type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;
}
ord_parameter_types! {
@@ -246,11 +230,7 @@
pub struct IsRegistered;
impl ValidatorRegistration<u64> for IsRegistered {
fn is_registered(id: &u64) -> bool {
- if *id == 7u64 {
- false
- } else {
- true
- }
+ *id != 7u64
}
}
@@ -265,6 +245,10 @@
type ValidatorIdOf = IdentityCollator;
type ValidatorRegistration = IsRegistered;
type LicenceBondIdentifier = LicenceBondIdentifier;
+ type Currency = Balances;
+ type DesiredCollators = MaxCollators;
+ type LicenseBond = LicenseBond;
+ type KickThreshold = KickThreshold;
type WeightInfo = ();
}
@@ -277,7 +261,11 @@
let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();
- let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (33, ed)];
+ let balances: Vec<(u64, u64)> = (1..=<Test as Config>::DesiredCollators::get() as u64 + 1)
+ .map(|i| (i, 100))
+ .chain(core::iter::once((33, ed)))
+ .collect();
+
let keys = balances
.iter()
.map(|&(i, _)| {
pallets/collator-selection/src/tests.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.3233use crate as collator_selection;34use crate::{mock::*, Error};35use frame_support::{36 assert_noop, assert_ok,37 traits::{fungible, GenesisBuild, OnInitialize},38};39use frame_system::RawOrigin;40use sp_runtime::{traits::BadOrigin, TokenError};41use pallet_configuration::{42 CollatorSelectionDesiredCollatorsOverride as DesiredCollators,43 CollatorSelectionKickThresholdOverride as KickThreshold,44 CollatorSelectionLicenseBondOverride as LicenseBond,45};46use scale_info::prelude::*;4748fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {49 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(50 account_id51 )));52 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(53 account_id54 )));55}5657#[test]58fn basic_setup_works() {59 new_test_ext().execute_with(|| {60 assert_eq!(<DesiredCollators<Test>>::get(), 5);61 assert_eq!(<LicenseBond<Test>>::get(), 10);6263 assert!(CollatorSelection::candidates().is_empty());64 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);65 });66}6768#[test]69fn it_should_add_invulnerables() {70 new_test_ext().execute_with(|| {71 assert_ok!(CollatorSelection::add_invulnerable(72 RuntimeOrigin::signed(RootAccount::get()),73 174 ));75 assert_ok!(CollatorSelection::add_invulnerable(76 RuntimeOrigin::signed(RootAccount::get()),77 278 ));79 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);8081 // cannot set with non-root.82 assert_noop!(83 CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), 3),84 BadOrigin85 );8687 // cannot set invulnerables without associated validator keys88 assert_noop!(89 CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 7),90 Error::<Test>::ValidatorNotRegistered91 );92 });93}9495#[test]96fn it_should_remove_invulnerables() {97 new_test_ext().execute_with(|| {98 assert_ok!(CollatorSelection::add_invulnerable(99 RuntimeOrigin::signed(RootAccount::get()),100 1101 ));102 assert_ok!(CollatorSelection::add_invulnerable(103 RuntimeOrigin::signed(RootAccount::get()),104 2105 ));106107 // cannot remove with non-root.108 assert_noop!(109 CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(1), 3),110 BadOrigin111 );112113 assert_ok!(CollatorSelection::remove_invulnerable(114 RuntimeOrigin::signed(RootAccount::get()),115 2116 ));117 assert_eq!(CollatorSelection::invulnerables(), vec![1]);118119 // cannot remove an invulnerable if there would be 0 invulnerables.120 assert_noop!(121 CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),122 Error::<Test>::TooFewInvulnerables123 );124 });125}126127#[test]128fn set_desired_collators_works() {129 new_test_ext().execute_with(|| {130 // given131 assert_eq!(<DesiredCollators<Test>>::get(), 5);132133 // can set134 assert_ok!(Configuration::set_collator_selection_desired_collators(135 RawOrigin::Root.into(),136 Some(7)137 ));138 assert_eq!(<DesiredCollators<Test>>::get(), 7);139140 // rejects bad origin141 assert_noop!(142 Configuration::set_collator_selection_desired_collators(143 RuntimeOrigin::signed(1),144 Some(8)145 ),146 BadOrigin147 );148 });149}150151#[test]152fn set_license_bond() {153 new_test_ext().execute_with(|| {154 // given155 assert_eq!(<LicenseBond<Test>>::get(), 10);156157 // can set158 assert_ok!(Configuration::set_collator_selection_license_bond(159 RawOrigin::Root.into(),160 Some(7)161 ));162 assert_eq!(<LicenseBond<Test>>::get(), 7);163164 // rejects bad origin.165 assert_noop!(166 Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)),167 BadOrigin168 );169 });170}171172#[test]173fn cannot_onboard_candidate_with_no_license() {174 new_test_ext().execute_with(|| {175 // can't onboard a candidate who did not get a license.176 assert_noop!(177 CollatorSelection::onboard(RuntimeOrigin::signed(3)),178 Error::<Test>::NoLicense,179 );180181 // but give it a license and welcome aboard.182 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));183 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(3)));184 })185}186187#[test]188fn cannot_onboard_candidate_if_too_many() {189 new_test_ext().execute_with(|| {190 // reset desired candidates191 <pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);192193 // can still get a license.194 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));195196 // can't accept anyone anymore.197 assert_noop!(198 CollatorSelection::onboard(RuntimeOrigin::signed(4)),199 Error::<Test>::TooManyCandidates,200 );201202 // reset desired candidates to invulnerables + 1203 <pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(3);204 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));205206 // but no more.207 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));208 assert_noop!(209 CollatorSelection::onboard(RuntimeOrigin::signed(5)),210 Error::<Test>::TooManyCandidates,211 );212 })213}214215#[test]216fn cannot_obtain_license_if_keys_not_registered() {217 new_test_ext().execute_with(|| {218 // can't 7 because keys not registered.219 assert_noop!(220 CollatorSelection::get_license(RuntimeOrigin::signed(7)),221 Error::<Test>::ValidatorNotRegistered222 );223 })224}225226#[test]227fn cannot_obtain_license_if_poor() {228 new_test_ext().execute_with(|| {229 let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();230 assert_eq!(Balances::free_balance(&3), 100);231 assert_eq!(Balances::free_balance(&33), ed);232233 // works234 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));235236 // poor237 assert_noop!(238 CollatorSelection::get_license(RuntimeOrigin::signed(33)),239 TokenError::FundsUnavailable,240 );241 });242}243244#[test]245fn cannot_onboard_dupe_candidate() {246 new_test_ext().execute_with(|| {247 // can add 3 as candidate248 get_license_and_onboard(3);249 assert_eq!(CollatorSelection::license_deposit_of(3), 10);250 assert_eq!(CollatorSelection::candidates(), vec![3]);251 assert_eq!(CollatorSelection::last_authored_block(3), 10);252 assert_eq!(Balances::free_balance(3), 90);253254 // but no more255 assert_noop!(256 CollatorSelection::get_license(RuntimeOrigin::signed(3)),257 Error::<Test>::AlreadyHoldingLicense,258 );259 assert_noop!(260 CollatorSelection::onboard(RuntimeOrigin::signed(3)),261 Error::<Test>::AlreadyCandidate,262 );263 })264}265266#[test]267fn becoming_candidate_works() {268 new_test_ext().execute_with(|| {269 // given270 assert_eq!(<DesiredCollators<Test>>::get(), 5);271 assert_eq!(<LicenseBond<Test>>::get(), 10);272 assert_eq!(CollatorSelection::candidates(), Vec::new());273 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);274275 // take two endowed, non-invulnerables accounts.276 assert_eq!(Balances::free_balance(&3), 100);277 assert_eq!(Balances::free_balance(&4), 100);278279 get_license_and_onboard(3);280 get_license_and_onboard(4);281282 assert_eq!(Balances::free_balance(&3), 90);283 assert_eq!(Balances::free_balance(&4), 90);284285 assert_eq!(CollatorSelection::candidates().len(), 2);286 });287}288289#[test]290fn cannot_become_candidate_if_invulnerable() {291 new_test_ext().execute_with(|| {292 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);293294 // can obtain a license even if is invulnerable.295 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(1)));296 // but cannot onboard297 assert_noop!(298 CollatorSelection::onboard(RuntimeOrigin::signed(1)),299 Error::<Test>::AlreadyInvulnerable,300 );301302 // get a license and then become invulnerable.303 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));304 assert_ok!(CollatorSelection::add_invulnerable(305 RuntimeOrigin::signed(RootAccount::get()),306 3307 ));308 assert_noop!(309 CollatorSelection::onboard(RuntimeOrigin::signed(3)),310 Error::<Test>::AlreadyInvulnerable,311 );312 })313}314315#[test]316fn can_become_invulnerable_if_candidate() {317 new_test_ext().execute_with(|| {318 // become a candidate and then become invulnerable.319 get_license_and_onboard(3);320 assert_eq!(CollatorSelection::candidates(), vec![3]);321322 assert_ok!(CollatorSelection::add_invulnerable(323 RuntimeOrigin::signed(RootAccount::get()),324 3325 ));326 // should exclude from candidates, but not revoke the license327 assert_eq!(CollatorSelection::candidates(), vec![]);328 assert_eq!(CollatorSelection::license_deposit_of(3), 10);329 assert_eq!(Balances::free_balance(3), 90);330 });331}332333#[test]334fn offboard() {335 new_test_ext().execute_with(|| {336 // register a candidate.337 get_license_and_onboard(3);338 assert_eq!(Balances::free_balance(3), 90);339340 // cannot leave if holds license but not yet candidate.341 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));342 assert_noop!(343 CollatorSelection::offboard(RuntimeOrigin::signed(4)),344 Error::<Test>::NotCandidate345 );346 // cannot leave if does not hold license.347 assert_noop!(348 CollatorSelection::offboard(RuntimeOrigin::signed(5)),349 Error::<Test>::NotCandidate350 );351352 // bond is returned - only after releasing the license353 assert_ok!(CollatorSelection::offboard(RuntimeOrigin::signed(3)));354 assert_eq!(Balances::free_balance(3), 90);355 assert_eq!(CollatorSelection::last_authored_block(3), 0);356 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));357 assert_eq!(Balances::free_balance(3), 100);358 });359}360361#[test]362fn release_license() {363 new_test_ext().execute_with(|| {364 // obtain a license to collate and reserve the bond.365 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));366 assert_eq!(Balances::free_balance(3), 90);367368 // release the license and get the bond back.369 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));370 assert_eq!(Balances::free_balance(3), 100);371372 // register a candidate.373 get_license_and_onboard(3);374 assert_eq!(Balances::free_balance(3), 90);375376 // can release license even if onboarded.377 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));378 assert_eq!(Balances::free_balance(3), 100);379 assert_eq!(CollatorSelection::candidates(), vec![]);380 });381}382383#[test]384fn force_release_license() {385 new_test_ext().execute_with(|| {386 // obtain a license to collate and reserve the bond.387 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));388 assert_eq!(Balances::free_balance(3), 90);389390 // cannot execute the operation as non-root391 assert_noop!(392 CollatorSelection::force_release_license(RuntimeOrigin::signed(3), 3),393 BadOrigin394 );395396 // release the license and get the bond back.397 assert_ok!(CollatorSelection::force_release_license(398 RuntimeOrigin::signed(RootAccount::get()),399 3400 ));401 assert_eq!(Balances::free_balance(3), 100);402403 // register a candidate.404 get_license_and_onboard(3);405 assert_eq!(Balances::free_balance(3), 90);406407 // can release license even if onboarded.408 assert_ok!(CollatorSelection::force_release_license(409 RuntimeOrigin::signed(RootAccount::get()),410 3411 ));412 assert_eq!(Balances::free_balance(3), 100);413 assert_eq!(CollatorSelection::candidates(), vec![]);414 });415}416417#[test]418fn authorship_event_handler() {419 new_test_ext().execute_with(|| {420 // put 100 in the pot + 5 for ED421 <Balances as fungible::Mutate<_>>::set_balance(&CollatorSelection::account_id(), 105);422423 // 4 is the default author.424 assert_eq!(Balances::free_balance(4), 100);425 get_license_and_onboard(4);426 // triggers `note_author`427 Authorship::on_initialize(1);428429 assert_eq!(CollatorSelection::candidates(), vec![4]);430 assert_eq!(CollatorSelection::last_authored_block(4), 0);431432 // half of the pot goes to the collator who's the author (4 in tests).433 assert_eq!(Balances::free_balance(4), 140);434 // half + ED stays.435 assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);436 });437}438439#[test]440fn fees_edgecases() {441 new_test_ext().execute_with(|| {442 // Nothing panics, no reward when no ED in balance443 Authorship::on_initialize(1);444 // put some money into the pot at ED445 <Balances as fungible::Mutate<_>>::set_balance(&CollatorSelection::account_id(), 5);446 // 4 is the default author.447 assert_eq!(Balances::free_balance(4), 100);448 get_license_and_onboard(4);449 // triggers `note_author`450 Authorship::on_initialize(1);451452 assert_eq!(CollatorSelection::candidates(), vec![4]);453 assert_eq!(CollatorSelection::last_authored_block(4), 0);454 // Nothing received455 assert_eq!(Balances::free_balance(4), 90);456 // all fee stays457 assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);458 });459}460461#[test]462fn session_management_works() {463 new_test_ext().execute_with(|| {464 initialize_to_block(1);465466 assert_eq!(SessionChangeBlock::get(), 0);467 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);468469 initialize_to_block(4);470471 assert_eq!(SessionChangeBlock::get(), 0);472 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);473474 // add a new collator475 get_license_and_onboard(5);476477 // session won't see this.478 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);479 // but we have a new candidate.480 assert_eq!(CollatorSelection::candidates().len(), 1);481482 initialize_to_block(10);483 assert_eq!(SessionChangeBlock::get(), 10);484 // pallet-session has 1 session delay; current validators are the same.485 assert_eq!(Session::validators(), vec![1, 2]);486 // queued ones are changed, and now we have 3.487 assert_eq!(Session::queued_keys().len(), 3);488 // session handlers (aura, et. al.) cannot see this yet.489 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);490491 initialize_to_block(20);492 assert_eq!(SessionChangeBlock::get(), 20);493 // changed are now reflected to session handlers.494 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);495 });496}497498#[test]499fn kick_mechanism() {500 new_test_ext().execute_with(|| {501 // add a new collator502 get_license_and_onboard(3);503 get_license_and_onboard(4);504505 initialize_to_block(10);506 assert_eq!(CollatorSelection::candidates().len(), 2);507508 initialize_to_block(20);509 assert_eq!(SessionChangeBlock::get(), 20);510 // 4 authored this block, gets to stay 3 was kicked511 assert_eq!(CollatorSelection::candidates().len(), 1);512 // 3 will be kicked after 1 session delay513 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);514515 assert_eq!(CollatorSelection::candidates(), vec![4]);516 assert_eq!(<KickThreshold<Test>>::get(), 10);517 assert_eq!(CollatorSelection::last_authored_block(4), 20);518519 initialize_to_block(30);520 // 3 gets kicked after 1 session delay521 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);522 // kicked collator gets their funds slashed, the deposit going to treasury523 assert_eq!(Balances::free_balance(3), 90);524 });525}526527#[test]528#[should_panic = "duplicate invulnerables in genesis."]529fn cannot_set_genesis_value_twice() {530 sp_tracing::try_init_simple();531 let mut t = frame_system::GenesisConfig::default()532 .build_storage::<Test>()533 .unwrap();534 let invulnerables = vec![1, 1];535536 let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };537 // collator selection must be initialized before session.538 collator_selection.assimilate_storage(&mut t).unwrap();539}1// 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.3233use crate::{self as collator_selection, Config};34use crate::{mock::*, Error};35use frame_support::{36 assert_noop, assert_ok,37 traits::{fungible, GenesisBuild, OnInitialize},38};39use sp_runtime::{traits::BadOrigin, TokenError};40use scale_info::prelude::*;4142fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {43 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(44 account_id45 )));46 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(47 account_id48 )));49}5051#[test]52fn basic_setup_works() {53 new_test_ext().execute_with(|| {54 assert!(CollatorSelection::candidates().is_empty());55 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);56 });57}5859#[test]60fn it_should_add_invulnerables() {61 new_test_ext().execute_with(|| {62 assert_ok!(CollatorSelection::add_invulnerable(63 RuntimeOrigin::signed(RootAccount::get()),64 165 ));66 assert_ok!(CollatorSelection::add_invulnerable(67 RuntimeOrigin::signed(RootAccount::get()),68 269 ));70 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);7172 // cannot set with non-root.73 assert_noop!(74 CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), 3),75 BadOrigin76 );7778 // cannot set invulnerables without associated validator keys79 assert_noop!(80 CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 7),81 Error::<Test>::ValidatorNotRegistered82 );83 });84}8586#[test]87fn it_should_remove_invulnerables() {88 new_test_ext().execute_with(|| {89 assert_ok!(CollatorSelection::add_invulnerable(90 RuntimeOrigin::signed(RootAccount::get()),91 192 ));93 assert_ok!(CollatorSelection::add_invulnerable(94 RuntimeOrigin::signed(RootAccount::get()),95 296 ));9798 // cannot remove with non-root.99 assert_noop!(100 CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(1), 3),101 BadOrigin102 );103104 assert_ok!(CollatorSelection::remove_invulnerable(105 RuntimeOrigin::signed(RootAccount::get()),106 2107 ));108 assert_eq!(CollatorSelection::invulnerables(), vec![1]);109110 // cannot remove an invulnerable if there would be 0 invulnerables.111 assert_noop!(112 CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),113 Error::<Test>::TooFewInvulnerables114 );115 });116}117118#[test]119fn cannot_onboard_candidate_with_no_license() {120 new_test_ext().execute_with(|| {121 // can't onboard a candidate who did not get a license.122 assert_noop!(123 CollatorSelection::onboard(RuntimeOrigin::signed(3)),124 Error::<Test>::NoLicense,125 );126127 // but give it a license and welcome aboard.128 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));129 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(3)));130 })131}132133#[test]134fn cannot_onboard_candidate_if_too_many() {135 new_test_ext().execute_with(|| {136 // can accept desired value of collators.137 for c in 3u64..=(<Test as Config>::DesiredCollators::get()).into() {138 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(c)));139 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(c)));140 }141142 // but no more.143 let undesired_collator = (<Test as Config>::DesiredCollators::get() + 1) as u64;144 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(145 undesired_collator146 )));147 assert_noop!(148 CollatorSelection::onboard(RuntimeOrigin::signed(undesired_collator)),149 Error::<Test>::TooManyCandidates,150 );151 })152}153154#[test]155fn cannot_obtain_license_if_keys_not_registered() {156 new_test_ext().execute_with(|| {157 // can't 7 because keys not registered.158 assert_noop!(159 CollatorSelection::get_license(RuntimeOrigin::signed(7)),160 Error::<Test>::ValidatorNotRegistered161 );162 })163}164165#[test]166fn cannot_obtain_license_if_poor() {167 new_test_ext().execute_with(|| {168 let ed = <Test as pallet_balances::Config>::ExistentialDeposit::get();169 assert_eq!(Balances::free_balance(3), 100);170 assert_eq!(Balances::free_balance(33), ed);171172 // works173 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));174175 // poor176 assert_noop!(177 CollatorSelection::get_license(RuntimeOrigin::signed(33)),178 TokenError::FundsUnavailable,179 );180 });181}182183#[test]184fn cannot_onboard_dupe_candidate() {185 new_test_ext().execute_with(|| {186 // can add 3 as candidate187 get_license_and_onboard(3);188 assert_eq!(CollatorSelection::license_deposit_of(3), 10);189 assert_eq!(CollatorSelection::candidates(), vec![3]);190 assert_eq!(CollatorSelection::last_authored_block(3), 10);191 assert_eq!(Balances::free_balance(3), 90);192193 // but no more194 assert_noop!(195 CollatorSelection::get_license(RuntimeOrigin::signed(3)),196 Error::<Test>::AlreadyHoldingLicense,197 );198 assert_noop!(199 CollatorSelection::onboard(RuntimeOrigin::signed(3)),200 Error::<Test>::AlreadyCandidate,201 );202 })203}204205#[test]206fn becoming_candidate_works() {207 new_test_ext().execute_with(|| {208 assert_eq!(CollatorSelection::candidates(), Vec::new());209 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);210211 // take two endowed, non-invulnerables accounts.212 assert_eq!(Balances::free_balance(3), 100);213 assert_eq!(Balances::free_balance(4), 100);214215 get_license_and_onboard(3);216 get_license_and_onboard(4);217218 assert_eq!(Balances::free_balance(3), 90);219 assert_eq!(Balances::free_balance(4), 90);220221 assert_eq!(CollatorSelection::candidates().len(), 2);222 });223}224225#[test]226fn cannot_become_candidate_if_invulnerable() {227 new_test_ext().execute_with(|| {228 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);229230 // can obtain a license even if is invulnerable.231 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(1)));232 // but cannot onboard233 assert_noop!(234 CollatorSelection::onboard(RuntimeOrigin::signed(1)),235 Error::<Test>::AlreadyInvulnerable,236 );237238 // get a license and then become invulnerable.239 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));240 assert_ok!(CollatorSelection::add_invulnerable(241 RuntimeOrigin::signed(RootAccount::get()),242 3243 ));244 assert_noop!(245 CollatorSelection::onboard(RuntimeOrigin::signed(3)),246 Error::<Test>::AlreadyInvulnerable,247 );248 })249}250251#[test]252fn can_become_invulnerable_if_candidate() {253 new_test_ext().execute_with(|| {254 // become a candidate and then become invulnerable.255 get_license_and_onboard(3);256 assert_eq!(CollatorSelection::candidates(), vec![3]);257258 assert_ok!(CollatorSelection::add_invulnerable(259 RuntimeOrigin::signed(RootAccount::get()),260 3261 ));262 // should exclude from candidates, but not revoke the license263 assert_eq!(CollatorSelection::candidates(), vec![]);264 assert_eq!(CollatorSelection::license_deposit_of(3), 10);265 assert_eq!(Balances::free_balance(3), 90);266 });267}268269#[test]270fn offboard() {271 new_test_ext().execute_with(|| {272 // register a candidate.273 get_license_and_onboard(3);274 assert_eq!(Balances::free_balance(3), 90);275276 // cannot leave if holds license but not yet candidate.277 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));278 assert_noop!(279 CollatorSelection::offboard(RuntimeOrigin::signed(4)),280 Error::<Test>::NotCandidate281 );282 // cannot leave if does not hold license.283 assert_noop!(284 CollatorSelection::offboard(RuntimeOrigin::signed(5)),285 Error::<Test>::NotCandidate286 );287288 // bond is returned - only after releasing the license289 assert_ok!(CollatorSelection::offboard(RuntimeOrigin::signed(3)));290 assert_eq!(Balances::free_balance(3), 90);291 assert_eq!(CollatorSelection::last_authored_block(3), 0);292 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));293 assert_eq!(Balances::free_balance(3), 100);294 });295}296297#[test]298fn release_license() {299 new_test_ext().execute_with(|| {300 // obtain a license to collate and reserve the bond.301 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));302 assert_eq!(Balances::free_balance(3), 90);303304 // release the license and get the bond back.305 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));306 assert_eq!(Balances::free_balance(3), 100);307308 // register a candidate.309 get_license_and_onboard(3);310 assert_eq!(Balances::free_balance(3), 90);311312 // can release license even if onboarded.313 assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));314 assert_eq!(Balances::free_balance(3), 100);315 assert_eq!(CollatorSelection::candidates(), vec![]);316 });317}318319#[test]320fn force_release_license() {321 new_test_ext().execute_with(|| {322 // obtain a license to collate and reserve the bond.323 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));324 assert_eq!(Balances::free_balance(3), 90);325326 // cannot execute the operation as non-root327 assert_noop!(328 CollatorSelection::force_release_license(RuntimeOrigin::signed(3), 3),329 BadOrigin330 );331332 // release the license and get the bond back.333 assert_ok!(CollatorSelection::force_release_license(334 RuntimeOrigin::signed(RootAccount::get()),335 3336 ));337 assert_eq!(Balances::free_balance(3), 100);338339 // register a candidate.340 get_license_and_onboard(3);341 assert_eq!(Balances::free_balance(3), 90);342343 // can release license even if onboarded.344 assert_ok!(CollatorSelection::force_release_license(345 RuntimeOrigin::signed(RootAccount::get()),346 3347 ));348 assert_eq!(Balances::free_balance(3), 100);349 assert_eq!(CollatorSelection::candidates(), vec![]);350 });351}352353#[test]354fn authorship_event_handler() {355 new_test_ext().execute_with(|| {356 // put 100 in the pot + 5 for ED357 <Balances as fungible::Mutate<_>>::set_balance(&CollatorSelection::account_id(), 105);358359 // 4 is the default author.360 assert_eq!(Balances::free_balance(4), 100);361 get_license_and_onboard(4);362 // triggers `note_author`363 Authorship::on_initialize(1);364365 assert_eq!(CollatorSelection::candidates(), vec![4]);366 assert_eq!(CollatorSelection::last_authored_block(4), 0);367368 // half of the pot goes to the collator who's the author (4 in tests).369 assert_eq!(Balances::free_balance(4), 140);370 // half + ED stays.371 assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);372 });373}374375#[test]376fn fees_edgecases() {377 new_test_ext().execute_with(|| {378 // Nothing panics, no reward when no ED in balance379 Authorship::on_initialize(1);380 // put some money into the pot at ED381 <Balances as fungible::Mutate<_>>::set_balance(&CollatorSelection::account_id(), 5);382 // 4 is the default author.383 assert_eq!(Balances::free_balance(4), 100);384 get_license_and_onboard(4);385 // triggers `note_author`386 Authorship::on_initialize(1);387388 assert_eq!(CollatorSelection::candidates(), vec![4]);389 assert_eq!(CollatorSelection::last_authored_block(4), 0);390 // Nothing received391 assert_eq!(Balances::free_balance(4), 90);392 // all fee stays393 assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);394 });395}396397#[test]398fn session_management_works() {399 new_test_ext().execute_with(|| {400 initialize_to_block(1);401402 assert_eq!(SessionChangeBlock::get(), 0);403 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);404405 initialize_to_block(4);406407 assert_eq!(SessionChangeBlock::get(), 0);408 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);409410 // add a new collator411 get_license_and_onboard(5);412413 // session won't see this.414 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);415 // but we have a new candidate.416 assert_eq!(CollatorSelection::candidates().len(), 1);417418 initialize_to_block(10);419 assert_eq!(SessionChangeBlock::get(), 10);420 // pallet-session has 1 session delay; current validators are the same.421 assert_eq!(Session::validators(), vec![1, 2]);422 // queued ones are changed, and now we have 3.423 assert_eq!(Session::queued_keys().len(), 3);424 // session handlers (aura, et. al.) cannot see this yet.425 assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);426427 initialize_to_block(20);428 assert_eq!(SessionChangeBlock::get(), 20);429 // changed are now reflected to session handlers.430 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);431 });432}433434#[test]435fn kick_mechanism() {436 new_test_ext().execute_with(|| {437 // add a new collator438 get_license_and_onboard(3);439 get_license_and_onboard(4);440441 initialize_to_block(10);442 assert_eq!(CollatorSelection::candidates().len(), 2);443444 initialize_to_block(20);445 assert_eq!(SessionChangeBlock::get(), 20);446 // 4 authored this block, gets to stay 3 was kicked447 assert_eq!(CollatorSelection::candidates().len(), 1);448 // 3 will be kicked after 1 session delay449 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);450451 assert_eq!(CollatorSelection::candidates(), vec![4]);452 // assert_eq!(<KickThreshold<Test>>::get(), 10);453 assert_eq!(CollatorSelection::last_authored_block(4), 20);454455 initialize_to_block(30);456 // 3 gets kicked after 1 session delay457 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);458 // kicked collator gets their funds slashed, the deposit going to treasury459 assert_eq!(Balances::free_balance(3), 90);460 });461}462463#[test]464#[should_panic = "duplicate invulnerables in genesis."]465fn cannot_set_genesis_value_twice() {466 sp_tracing::try_init_simple();467 let mut t = frame_system::GenesisConfig::default()468 .build_storage::<Test>()469 .unwrap();470 let invulnerables = vec![1, 1];471472 let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };473 // collator selection must be initialized before session.474 collator_selection.assimilate_storage(&mut t).unwrap();475}pallets/configuration/CHANGELOG.mddiffbeforeafterboth--- a/pallets/configuration/CHANGELOG.md
+++ b/pallets/configuration/CHANGELOG.md
@@ -3,7 +3,12 @@
All notable changes to this project will be documented in this file.
<!-- bureaucrate goes here -->
+## [0.2.0] - 2023-06-27
+
+### Major change
+- Architecture fixed: in the configuration of this pallet, bounds on the associated type were determined not by the functional requirements for this pallet itself, but by the pallet that had tight coupling with it.
+
## [0.1.3] - 2022-12-29
### Added
@@ -20,4 +25,4 @@
### Other changes
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
\ No newline at end of file
+- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
pallets/configuration/Cargo.tomldiffbeforeafterboth--- a/pallets/configuration/Cargo.toml
+++ b/pallets/configuration/Cargo.toml
@@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "pallet-configuration"
-version = "0.1.3"
+version = "0.2.0"
[dependencies]
# Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it.
@@ -22,11 +22,11 @@
default = ["std"]
runtime-benchmarks = ["frame-benchmarking"]
std = [
+ "codec/std",
"fp-evm/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
- "codec/std",
"sp-arithmetic/std",
"sp-core/std",
"sp-std/std",
pallets/configuration/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/configuration/src/benchmarking.rs
+++ b/pallets/configuration/src/benchmarking.rs
@@ -19,7 +19,7 @@
use super::*;
use frame_benchmarking::benchmarks;
use frame_system::{EventRecord, RawOrigin};
-use frame_support::{assert_ok, traits::fungible::Inspect};
+use frame_support::assert_ok;
fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
@@ -30,7 +30,10 @@
}
benchmarks! {
- where_clause { where T: Config }
+ where_clause { where
+ T: Config,
+ T::Balance: From<u32>
+ }
set_weight_to_fee_coefficient_override {
let coeff: u64 = 999;
@@ -60,7 +63,7 @@
let max: u32 = 999;
}: {
assert_ok!(
- <Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max.clone()))
+ <Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max))
);
}
verify {
@@ -68,10 +71,10 @@
}
set_collator_selection_license_bond {
- let bond_cost: Option<BalanceOf<T>> = Some(T::Currency::minimum_balance() * 10u32.into());
+ let bond_cost: Option<T::Balance> = Some(1000u32.into());
}: {
assert_ok!(
- <Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost.clone())
+ <Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost)
);
}
verify {
@@ -82,7 +85,7 @@
let threshold: Option<T::BlockNumber> = Some(900u32.into());
}: {
assert_ok!(
- <Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold.clone())
+ <Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold)
);
}
verify {
pallets/configuration/src/lib.rsdiffbeforeafterboth--- a/pallets/configuration/src/lib.rs
+++ b/pallets/configuration/src/lib.rs
@@ -22,6 +22,7 @@
pallet,
weights::{WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient, Weight},
traits::Get,
+ Parameter,
};
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
@@ -42,27 +43,33 @@
mod pallet {
use super::*;
use frame_support::{
- traits::{fungible, Get},
- pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType},
+ traits::{Get},
+ pallet_prelude::{
+ StorageValue, ValueQuery, DispatchResult, IsType, Member, MaybeSerializeDeserialize,
+ },
log,
+ dispatch::{Codec, fmt::Debug},
};
- use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};
-
+ use frame_system::{pallet_prelude::OriginFor, ensure_root};
+ use sp_arithmetic::{FixedPointOperand, traits::AtLeast32BitUnsigned};
pub use crate::weights::WeightInfo;
- pub type BalanceOf<T> =
- <<T as Config>::Currency as fungible::Inspect<<T as SystemConfig>::AccountId>>::Balance;
#[pallet::config]
pub trait Config: frame_system::Config {
/// Overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
- type Currency: fungible::Inspect<Self::AccountId>
- + fungible::Mutate<Self::AccountId>
- + fungible::MutateFreeze<Self::AccountId>
- + fungible::InspectHold<Self::AccountId>
- + fungible::MutateHold<Self::AccountId>
- + fungible::BalancedHold<Self::AccountId>;
+ type Balance: Parameter
+ + Member
+ + AtLeast32BitUnsigned
+ + Codec
+ + Default
+ + Copy
+ + MaybeSerializeDeserialize
+ + Debug
+ + MaxEncodedLen
+ + TypeInfo
+ + FixedPointOperand;
#[pallet::constant]
type DefaultWeightToFeeCoefficient: Get<u64>;
@@ -79,7 +86,7 @@
#[pallet::constant]
type DefaultCollatorSelectionMaxCollators: Get<u32>;
#[pallet::constant]
- type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;
+ type DefaultCollatorSelectionLicenseBond: Get<Self::Balance>;
#[pallet::constant]
type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;
@@ -94,7 +101,7 @@
desired_collators: Option<u32>,
},
NewCollatorLicenseBond {
- bond_cost: Option<BalanceOf<T>>,
+ bond_cost: Option<T::Balance>,
},
NewCollatorKickThreshold {
length_in_blocks: Option<T::BlockNumber>,
@@ -130,7 +137,7 @@
#[pallet::storage]
pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<
- Value = BalanceOf<T>,
+ Value = T::Balance,
QueryKind = ValueQuery,
OnEmpty = T::DefaultCollatorSelectionLicenseBond,
>;
@@ -221,7 +228,7 @@
#[pallet::weight(T::WeightInfo::set_collator_selection_license_bond())]
pub fn set_collator_selection_license_bond(
origin: OriginFor<T>,
- amount: Option<BalanceOf<T>>,
+ amount: Option<<T as Config>::Balance>,
) -> DispatchResult {
ensure_root(origin)?;
if let Some(amount) = amount {
runtime/common/config/pallets/collator_selection.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/collator_selection.rs
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -23,7 +23,10 @@
};
use sp_runtime::Perbill;
use up_common::constants::{UNIQUE, MILLIUNIQUE};
-
+use pallet_configuration::{
+ CollatorSelectionKickThresholdOverride, CollatorSelectionLicenseBondOverride,
+ CollatorSelectionDesiredCollatorsOverride,
+};
parameter_types! {
pub const SessionOffset: BlockNumber = 0;
}
@@ -60,6 +63,9 @@
pub const MaxAdditionalFields: u32 = 100;
pub const MaxRegistrars: u32 = 20;
pub const LicenceBondIdentifier: [u8; 16] = *b"licenceidentifie";
+ pub LicenseBond: Balance = CollatorSelectionLicenseBondOverride::<Runtime>::get();
+ pub DesiredCollators: u32 = CollatorSelectionDesiredCollatorsOverride::<Runtime>::get();
+ pub KickThreshold: BlockNumber = CollatorSelectionKickThresholdOverride::<Runtime>::get();
}
impl pallet_identity::Config for Runtime {
@@ -84,6 +90,7 @@
impl pallet_collator_selection::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
+ type Currency = Balances;
// We allow root only to execute privileged collator selection operations.
type UpdateOrigin = EnsureRoot<AccountId>;
type TreasuryAccountId = TreasuryAccountId;
@@ -95,4 +102,7 @@
type ValidatorRegistration = Session;
type WeightInfo = pallet_collator_selection::weights::SubstrateWeight<Runtime>;
type LicenceBondIdentifier = LicenceBondIdentifier;
+ type DesiredCollators = DesiredCollators;
+ type LicenseBond = LicenseBond;
+ type KickThreshold = KickThreshold;
}
runtime/common/config/pallets/mod.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -129,7 +129,7 @@
impl pallet_configuration::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
- type Currency = Balances;
+ type Balance = Balance;
type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;
type DefaultCollatorSelectionMaxCollators = MaxCollators;
tests/src/benchmarks/mintFee/index.tsdiffbeforeafterboth--- a/tests/src/benchmarks/mintFee/index.ts
+++ b/tests/src/benchmarks/mintFee/index.ts
@@ -360,7 +360,7 @@
susbstrateReceiver.addressRaw,
PROPERTIES.slice(0, setup.propertiesNumber),
)
- .send({from: ethSigner, gas: 25_000_000});
+ .send({from: ethSigner});
},
);