git.delta.rocks / unique-network / refs/commits / ce4b05d34132

difftreelog

source

pallets/collator-selection/src/tests.rs15.8 KiBsourcehistory
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 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}