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

difftreelog

source

pallets/collator-selection/src/tests.rs15.6 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::{Currency, GenesisBuild, OnInitialize},38};39use pallet_balances::Error as BalancesError;40use sp_runtime::traits::BadOrigin;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_eq!(CollatorSelection::desired_collators(), 5);55		assert_eq!(CollatorSelection::license_bond(), 10);5657		assert!(CollatorSelection::candidates().is_empty());58		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);59	});60}6162// todo:collator add more tests later63// invulnerable after onboard + invulnerables can bypass desired_candidates6465#[test]66fn it_should_add_invulnerables() {67	new_test_ext().execute_with(|| {68		assert_ok!(CollatorSelection::add_invulnerable(69			RuntimeOrigin::signed(RootAccount::get()),70			171		));72		assert_ok!(CollatorSelection::add_invulnerable(73			RuntimeOrigin::signed(RootAccount::get()),74			275		));76		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);7778		// cannot set with non-root.79		assert_noop!(80			CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), 3),81			BadOrigin82		);8384		// cannot set invulnerables without associated validator keys85		assert_noop!(86			CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 7),87			Error::<Test>::ValidatorNotRegistered88		);89	});90}9192#[test]93fn it_should_remove_invulnerables() {94	new_test_ext().execute_with(|| {95		assert_ok!(CollatorSelection::add_invulnerable(96			RuntimeOrigin::signed(RootAccount::get()),97			198		));99		assert_ok!(CollatorSelection::add_invulnerable(100			RuntimeOrigin::signed(RootAccount::get()),101			2102		));103104		// cannot remove with non-root.105		assert_noop!(106			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(1), 3),107			BadOrigin108		);109110		assert_ok!(CollatorSelection::remove_invulnerable(111			RuntimeOrigin::signed(RootAccount::get()),112			2113		));114		assert_eq!(CollatorSelection::invulnerables(), vec![1]);115116		// cannot remove an invulnerable if there would be 0 invulnerables.117		assert_noop!(118			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),119			Error::<Test>::TooFewInvulnerables120		);121	});122}123124#[test]125fn set_desired_collators_works() {126	new_test_ext().execute_with(|| {127		// given128		assert_eq!(CollatorSelection::desired_collators(), 5);129130		// can set131		assert_ok!(CollatorSelection::set_desired_collators(132			RuntimeOrigin::signed(RootAccount::get()),133			7134		));135		assert_eq!(CollatorSelection::desired_collators(), 7);136137		// rejects bad origin138		assert_noop!(139			CollatorSelection::set_desired_collators(RuntimeOrigin::signed(1), 8),140			BadOrigin141		);142	});143}144145#[test]146fn set_license_bond() {147	new_test_ext().execute_with(|| {148		// given149		assert_eq!(CollatorSelection::license_bond(), 10);150151		// can set152		assert_ok!(CollatorSelection::set_license_bond(153			RuntimeOrigin::signed(RootAccount::get()),154			7155		));156		assert_eq!(CollatorSelection::license_bond(), 7);157158		// rejects bad origin.159		assert_noop!(160			CollatorSelection::set_license_bond(RuntimeOrigin::signed(1), 8),161			BadOrigin162		);163	});164}165166#[test]167fn cannot_onboard_candidate_with_no_license() {168	new_test_ext().execute_with(|| {169		// can't onboard a candidate who did not get a license.170		assert_noop!(171			CollatorSelection::onboard(RuntimeOrigin::signed(3)),172			Error::<Test>::NoLicense,173		);174175		// but give it a license and welcome aboard.176		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));177		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(3)));178	})179}180181#[test]182fn cannot_onboard_candidate_if_too_many() {183	new_test_ext().execute_with(|| {184		// reset desired candidates185		<crate::DesiredCollators<Test>>::put(0);186187		// can still get a license.188		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));189190		// can't accept anyone anymore.191		assert_noop!(192			CollatorSelection::onboard(RuntimeOrigin::signed(4)),193			Error::<Test>::TooManyCandidates,194		);195196		// reset desired candidates to invulnerables + 1197		<crate::DesiredCollators<Test>>::put(3);198		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));199200		// but no more.201		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));202		assert_noop!(203			CollatorSelection::onboard(RuntimeOrigin::signed(5)),204			Error::<Test>::TooManyCandidates,205		);206	})207}208209#[test]210fn cannot_obtain_license_if_keys_not_registered() {211	new_test_ext().execute_with(|| {212		// can't 7 because keys not registered.213		assert_noop!(214			CollatorSelection::get_license(RuntimeOrigin::signed(7)),215			Error::<Test>::ValidatorNotRegistered216		);217	})218}219220#[test]221fn cannot_obtain_license_if_poor() {222	new_test_ext().execute_with(|| {223		assert_eq!(Balances::free_balance(&3), 100);224		assert_eq!(Balances::free_balance(&33), 0);225226		// works227		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));228229		// poor230		assert_noop!(231			CollatorSelection::get_license(RuntimeOrigin::signed(33)),232			BalancesError::<Test>::InsufficientBalance,233		);234	});235}236237#[test]238fn cannot_onboard_dupe_candidate() {239	new_test_ext().execute_with(|| {240		// can add 3 as candidate241		get_license_and_onboard(3);242		assert_eq!(CollatorSelection::licenses(3), 10);243		assert_eq!(CollatorSelection::candidates(), vec![3]);244		assert_eq!(CollatorSelection::last_authored_block(3), 10);245		assert_eq!(Balances::free_balance(3), 90);246247		// but no more248		assert_noop!(249			CollatorSelection::get_license(RuntimeOrigin::signed(3)),250			Error::<Test>::AlreadyHoldingLicense,251		);252		assert_noop!(253			CollatorSelection::onboard(RuntimeOrigin::signed(3)),254			Error::<Test>::AlreadyCandidate,255		);256	})257}258259#[test]260fn becoming_candidate_works() {261	new_test_ext().execute_with(|| {262		// given263		assert_eq!(CollatorSelection::desired_collators(), 5);264		assert_eq!(CollatorSelection::license_bond(), 10);265		assert_eq!(CollatorSelection::candidates(), Vec::new());266		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);267268		// take two endowed, non-invulnerables accounts.269		assert_eq!(Balances::free_balance(&3), 100);270		assert_eq!(Balances::free_balance(&4), 100);271272		get_license_and_onboard(3);273		get_license_and_onboard(4);274275		assert_eq!(Balances::free_balance(&3), 90);276		assert_eq!(Balances::free_balance(&4), 90);277278		assert_eq!(CollatorSelection::candidates().len(), 2);279	});280}281282#[test]283fn cannot_become_candidate_if_invulnerable() {284	new_test_ext().execute_with(|| {285		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);286287		// can obtain a license even if is invulnerable.288		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(1)));289		// but cannot onboard290		assert_noop!(291			CollatorSelection::onboard(RuntimeOrigin::signed(1)),292			Error::<Test>::AlreadyInvulnerable,293		);294295		// get a license and then become invulnerable.296		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));297		assert_ok!(CollatorSelection::add_invulnerable(298			RuntimeOrigin::signed(RootAccount::get()),299			3300		));301		assert_noop!(302			CollatorSelection::onboard(RuntimeOrigin::signed(3)),303			Error::<Test>::AlreadyInvulnerable,304		);305	})306}307308#[test]309fn can_become_invulnerable_if_candidate() {310	new_test_ext().execute_with(|| {311		// become a candidate and then become invulnerable.312		get_license_and_onboard(3);313		assert_eq!(CollatorSelection::candidates(), vec![3]);314315		assert_ok!(CollatorSelection::add_invulnerable(316			RuntimeOrigin::signed(RootAccount::get()),317			3318		));319		// should exclude from candidates, but not revoke the license320		assert_eq!(CollatorSelection::candidates(), vec![]);321		assert_eq!(CollatorSelection::licenses(3), 10);322		assert_eq!(Balances::free_balance(3), 90);323	});324}325326#[test]327fn offboard() {328	new_test_ext().execute_with(|| {329		// register a candidate.330		get_license_and_onboard(3);331		assert_eq!(Balances::free_balance(3), 90);332333		// cannot leave if holds license but not yet candidate.334		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));335		assert_noop!(336			CollatorSelection::offboard(RuntimeOrigin::signed(4)),337			Error::<Test>::NotCandidate338		);339		// cannot leave if does not hold license.340		assert_noop!(341			CollatorSelection::offboard(RuntimeOrigin::signed(5)),342			Error::<Test>::NotCandidate343		);344345		// bond is returned - only after releasing the license346		assert_ok!(CollatorSelection::offboard(RuntimeOrigin::signed(3)));347		assert_eq!(Balances::free_balance(3), 90);348		assert_eq!(CollatorSelection::last_authored_block(3), 0);349		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));350		assert_eq!(Balances::free_balance(3), 100);351	});352}353354#[test]355fn release_license() {356	new_test_ext().execute_with(|| {357		// obtain a license to collate and reserve the bond.358		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));359		assert_eq!(Balances::free_balance(3), 90);360361		// release the license and get the bond back.362		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));363		assert_eq!(Balances::free_balance(3), 100);364365		// register a candidate.366		get_license_and_onboard(3);367		assert_eq!(Balances::free_balance(3), 90);368369		// can release license even if onboarded.370		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));371		assert_eq!(Balances::free_balance(3), 100);372		assert_eq!(CollatorSelection::candidates(), vec![]);373	});374}375376#[test]377fn force_revoke_license() {378	new_test_ext().execute_with(|| {379		// obtain a license to collate and reserve the bond.380		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));381		assert_eq!(Balances::free_balance(3), 90);382383		// cannot execute the operation as non-root384		assert_noop!(385			CollatorSelection::force_revoke_license(RuntimeOrigin::signed(3), 3),386			BadOrigin387		);388389		// release the license and get the bond back.390		assert_ok!(CollatorSelection::force_revoke_license(391			RuntimeOrigin::signed(RootAccount::get()),392			3393		));394		assert_eq!(Balances::free_balance(3), 100);395396		// register a candidate.397		get_license_and_onboard(3);398		assert_eq!(Balances::free_balance(3), 90);399400		// can release license even if onboarded.401		assert_ok!(CollatorSelection::force_revoke_license(402			RuntimeOrigin::signed(RootAccount::get()),403			3404		));405		assert_eq!(Balances::free_balance(3), 100);406		assert_eq!(CollatorSelection::candidates(), vec![]);407	});408}409410#[test]411fn authorship_event_handler() {412	new_test_ext().execute_with(|| {413		// put 100 in the pot + 5 for ED414		Balances::make_free_balance_be(&CollatorSelection::account_id(), 105);415416		// 4 is the default author.417		assert_eq!(Balances::free_balance(4), 100);418		get_license_and_onboard(4);419		// triggers `note_author`420		Authorship::on_initialize(1);421422		assert_eq!(CollatorSelection::candidates(), vec![4]);423		assert_eq!(CollatorSelection::last_authored_block(4), 0);424425		// half of the pot goes to the collator who's the author (4 in tests).426		assert_eq!(Balances::free_balance(4), 140);427		// half + ED stays.428		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);429	});430}431432#[test]433fn fees_edgecases() {434	new_test_ext().execute_with(|| {435		// Nothing panics, no reward when no ED in balance436		Authorship::on_initialize(1);437		// put some money into the pot at ED438		Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);439		// 4 is the default author.440		assert_eq!(Balances::free_balance(4), 100);441		get_license_and_onboard(4);442		// triggers `note_author`443		Authorship::on_initialize(1);444445		assert_eq!(CollatorSelection::candidates(), vec![4]);446		assert_eq!(CollatorSelection::last_authored_block(4), 0);447		// Nothing received448		assert_eq!(Balances::free_balance(4), 90);449		// all fee stays450		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);451	});452}453454#[test]455fn session_management_works() {456	new_test_ext().execute_with(|| {457		initialize_to_block(1);458459		assert_eq!(SessionChangeBlock::get(), 0);460		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);461462		initialize_to_block(4);463464		assert_eq!(SessionChangeBlock::get(), 0);465		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);466467		// add a new collator468		get_license_and_onboard(5);469470		// session won't see this.471		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);472		// but we have a new candidate.473		assert_eq!(CollatorSelection::candidates().len(), 1);474475		initialize_to_block(10);476		assert_eq!(SessionChangeBlock::get(), 10);477		// pallet-session has 1 session delay; current validators are the same.478		assert_eq!(Session::validators(), vec![1, 2]);479		// queued ones are changed, and now we have 3.480		assert_eq!(Session::queued_keys().len(), 3);481		// session handlers (aura, et. al.) cannot see this yet.482		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);483484		initialize_to_block(20);485		assert_eq!(SessionChangeBlock::get(), 20);486		// changed are now reflected to session handlers.487		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);488	});489}490491#[test]492fn kick_mechanism() {493	new_test_ext().execute_with(|| {494		// add a new collator495		get_license_and_onboard(3);496		get_license_and_onboard(4);497498		initialize_to_block(10);499		assert_eq!(CollatorSelection::candidates().len(), 2);500501		initialize_to_block(20);502		assert_eq!(SessionChangeBlock::get(), 20);503		// 4 authored this block, gets to stay 3 was kicked504		assert_eq!(CollatorSelection::candidates().len(), 1);505		// 3 will be kicked after 1 session delay506		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);507508		assert_eq!(CollatorSelection::candidates(), vec![4]);509		assert_eq!(CollatorSelection::kick_threshold(), 10);510		assert_eq!(CollatorSelection::last_authored_block(4), 20);511512		initialize_to_block(30);513		// 3 gets kicked after 1 session delay514		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);515		// kicked collator gets their funds slashed, the deposit going to treasury516		assert_eq!(Balances::free_balance(3), 90);517	});518}519520#[test]521#[should_panic = "duplicate invulnerables in genesis."]522fn cannot_set_genesis_value_twice() {523	sp_tracing::try_init_simple();524	let mut t = frame_system::GenesisConfig::default()525		.build_storage::<Test>()526		.unwrap();527	let invulnerables = vec![1, 1];528529	let collator_selection = collator_selection::GenesisConfig::<Test> {530		desired_collators: 5,531		license_bond: 10,532		kick_threshold: 10,533		invulnerables,534	};535	// collator selection must be initialized before session.536	collator_selection.assimilate_storage(&mut t).unwrap();537}