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

difftreelog

source

pallets/collator-selection/src/tests.rs14.3 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 frame_support::{34	assert_noop, assert_ok,35	traits::{fungible, OnInitialize},36};37use scale_info::prelude::*;38use sp_runtime::{traits::BadOrigin, BuildStorage, TokenError};3940use crate::{self as collator_selection, mock::*, Config, Error};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<Test>>::default()468		.build_storage()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}