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

difftreelog

fix collator-selection tests

Daniel Shiposha2023-09-08parent: #d892f52.patch.diff
in: master

8 files changed

modified.baedeker/collator-selection.jsonnetdiffbeforeafterboth
--- a/.baedeker/collator-selection.jsonnet
+++ b/.baedeker/collator-selection.jsonnet
@@ -31,7 +31,7 @@
 			bin: $.bin,
 			wantedKeys: 'para',
 		},
-		for name in ['alice', 'bob']
+		for name in ['alice', 'bob', 'charlie', 'dave']
 	},
 };
 
added.baedeker/node-stash-discover.jsonnetdiffbeforeafterboth
--- /dev/null
+++ b/.baedeker/node-stash-discover.jsonnet
@@ -0,0 +1,13 @@
+local {flattenNodes, flattenChains, ...} = import 'baedeker-library/util/mixin.libsonnet';
+
+function(prev, final)
+prev + {
+	_output+:: {
+		dockerComposeDiscover+: std.join('\n', [
+			'%s_STASH=%s' % [std.strReplace(std.asciiUpper(node.hostname), '-', '_'), node.wallets.stash]
+			for chain in flattenChains(prev)
+			if 'paraId' in chain
+			for node in flattenNodes(chain)
+		] + ['']),
+	},
+}
modified.github/workflows/collator-selection.ymldiffbeforeafterboth
--- a/.github/workflows/collator-selection.yml
+++ b/.github/workflows/collator-selection.yml
@@ -139,7 +139,7 @@
           context: .
           dockerfile: .docker/Dockerfile-unique
           args: |
-            --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }}
+            --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }},${{ matrix.wasm_name }}/session-test-timing
             --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
           dockerhub_username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}
           dockerhub_token: ${{ secrets.CORE_DOCKERHUB_TOKEN }}
@@ -164,6 +164,7 @@
             relay_spec=westend-local            
           inputs: |
             .baedeker/collator-selection.jsonnet
+            .baedeker/node-stash-discover.jsonnet
             snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/polkadot':{dockerImage:'${{ steps.polkadot.outputs.name }}'}})
             snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/unique':{dockerImage:'${{ steps.latest.outputs.name }}'}})
 
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
before · runtime/common/config/pallets/mod.rs
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/>.1617use alloc::string::{String, ToString};18use frame_support::parameter_types;19use sp_runtime::traits::AccountIdConversion;20use crate::{21	runtime_common::{22		dispatch::CollectionDispatchT,23		config::{substrate::TreasuryModuleId, ethereum::EvmCollectionHelpersAddress},24		weights::CommonWeights,25		RelayChainBlockNumberProvider,26	},27	Runtime, RuntimeEvent, RuntimeCall, VERSION, TOKEN_SYMBOL, DECIMALS, Balances,28};29use frame_support::traits::{ConstU32, ConstU64, Currency};30use up_common::{31	types::{AccountId, Balance, BlockNumber},32	constants::*,33};34use up_data_structs::{35	mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},36};37use sp_arithmetic::Perbill;3839#[cfg(feature = "governance")]40use crate::runtime_common::config::governance;4142#[cfg(feature = "unique-scheduler")]43pub mod scheduler;4445#[cfg(feature = "foreign-assets")]46pub mod foreign_asset;4748#[cfg(feature = "app-promotion")]49pub mod app_promotion;5051#[cfg(feature = "collator-selection")]52pub mod collator_selection;5354#[cfg(feature = "preimage")]55pub mod preimage;5657parameter_types! {58	pub const CollectionCreationPrice: Balance = 2 * UNIQUE;59	pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();60}6162impl pallet_common::Config for Runtime {63	type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;64	type RuntimeEvent = RuntimeEvent;65	type Currency = Balances;66	type CollectionCreationPrice = CollectionCreationPrice;67	type TreasuryAccountId = TreasuryAccountId;68	type CollectionDispatch = CollectionDispatchT<Self>;6970	type EvmTokenAddressMapping = EvmTokenAddressMapping;71	type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;72	type ContractAddress = EvmCollectionHelpersAddress;73}7475impl pallet_structure::Config for Runtime {76	type RuntimeEvent = RuntimeEvent;77	type RuntimeCall = RuntimeCall;78	type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;79}8081impl pallet_fungible::Config for Runtime {82	type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;83}84impl pallet_refungible::Config for Runtime {85	type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;86}87impl pallet_nonfungible::Config for Runtime {88	type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;89}9091parameter_types! {92	pub const Decimals: u8 = DECIMALS;93	pub Name: String = String::from_utf8_lossy(VERSION.impl_name.as_ref()).to_string();94	pub Symbol: String = TOKEN_SYMBOL.to_string();95}96impl pallet_balances_adapter::Config for Runtime {97	type Inspect = Balances;98	type Mutate = Balances;99	type CurrencyBalance = <Balances as Currency<Self::AccountId>>::Balance;100	type Decimals = Decimals;101	type Name = Name;102	type Symbol = Symbol;103	type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;104}105106parameter_types! {107	pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied108}109110/// Used for the pallet inflation111impl pallet_inflation::Config for Runtime {112	type Currency = Balances;113	type TreasuryAccountId = TreasuryAccountId;114	type InflationBlockInterval = InflationBlockInterval;115	type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;116}117118impl pallet_unique::Config for Runtime {119	type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;120	type CommonWeightInfo = CommonWeights<Self>;121	type RefungibleExtensionsWeightInfo = CommonWeights<Self>;122}123124parameter_types! {125	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);126	pub const MaxCollators: u32 = MAX_COLLATORS;127	pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;128	pub const SessionPeriod: BlockNumber = SESSION_LENGTH;129	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;130}131132impl pallet_configuration::Config for Runtime {133	type RuntimeEvent = RuntimeEvent;134	type Balance = Balance;135	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;136	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;137	type DefaultCollatorSelectionMaxCollators = MaxCollators;138	type DefaultCollatorSelectionKickThreshold = SessionPeriod;139	type DefaultCollatorSelectionLicenseBond = LicenseBond;140	type MaxXcmAllowedLocations = ConstU32<16>;141	type AppPromotionDailyRate = AppPromotionDailyRate;142	type DayRelayBlocks = DayRelayBlocks;143	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;144}145146impl pallet_maintenance::Config for Runtime {147	type RuntimeEvent = RuntimeEvent;148149	type RuntimeCall = RuntimeCall;150151	#[cfg(feature = "governance")]152	type ManagerOrigin = governance::RootOrTechnicalCommitteeMember;153154	#[cfg(not(feature = "governance"))]155	type ManagerOrigin = frame_system::EnsureRoot<AccountId>;156157	type PreimageOrigin = frame_system::EnsureRoot<AccountId>;158159	#[cfg(feature = "preimage")]160	type Preimages = crate::Preimage;161	#[cfg(not(feature = "preimage"))]162	type Preimages = ();163	type WeightInfo = pallet_maintenance::weights::SubstrateWeight<Self>;164}
after · runtime/common/config/pallets/mod.rs
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/>.1617use alloc::string::{String, ToString};18use frame_support::parameter_types;19use sp_runtime::traits::AccountIdConversion;20use crate::{21	runtime_common::{22		dispatch::CollectionDispatchT,23		config::{substrate::TreasuryModuleId, ethereum::EvmCollectionHelpersAddress},24		weights::CommonWeights,25		RelayChainBlockNumberProvider,26	},27	Runtime, RuntimeEvent, RuntimeCall, VERSION, TOKEN_SYMBOL, DECIMALS, Balances,28};29use frame_support::traits::{ConstU32, ConstU64, Currency};30use up_common::{31	types::{AccountId, Balance, BlockNumber},32	constants::*,33};34use up_data_structs::{35	mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},36};37use sp_arithmetic::Perbill;3839#[cfg(feature = "governance")]40use crate::runtime_common::config::governance;4142#[cfg(feature = "unique-scheduler")]43pub mod scheduler;4445#[cfg(feature = "foreign-assets")]46pub mod foreign_asset;4748#[cfg(feature = "app-promotion")]49pub mod app_promotion;5051#[cfg(feature = "collator-selection")]52pub mod collator_selection;5354#[cfg(feature = "preimage")]55pub mod preimage;5657parameter_types! {58	pub const CollectionCreationPrice: Balance = 2 * UNIQUE;59	pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();60}6162impl pallet_common::Config for Runtime {63	type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;64	type RuntimeEvent = RuntimeEvent;65	type Currency = Balances;66	type CollectionCreationPrice = CollectionCreationPrice;67	type TreasuryAccountId = TreasuryAccountId;68	type CollectionDispatch = CollectionDispatchT<Self>;6970	type EvmTokenAddressMapping = EvmTokenAddressMapping;71	type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;72	type ContractAddress = EvmCollectionHelpersAddress;73}7475impl pallet_structure::Config for Runtime {76	type RuntimeEvent = RuntimeEvent;77	type RuntimeCall = RuntimeCall;78	type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;79}8081impl pallet_fungible::Config for Runtime {82	type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;83}84impl pallet_refungible::Config for Runtime {85	type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;86}87impl pallet_nonfungible::Config for Runtime {88	type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;89}9091parameter_types! {92	pub const Decimals: u8 = DECIMALS;93	pub Name: String = String::from_utf8_lossy(VERSION.impl_name.as_ref()).to_string();94	pub Symbol: String = TOKEN_SYMBOL.to_string();95}96impl pallet_balances_adapter::Config for Runtime {97	type Inspect = Balances;98	type Mutate = Balances;99	type CurrencyBalance = <Balances as Currency<Self::AccountId>>::Balance;100	type Decimals = Decimals;101	type Name = Name;102	type Symbol = Symbol;103	type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;104}105106parameter_types! {107	pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied108}109110/// Used for the pallet inflation111impl pallet_inflation::Config for Runtime {112	type Currency = Balances;113	type TreasuryAccountId = TreasuryAccountId;114	type InflationBlockInterval = InflationBlockInterval;115	type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;116}117118impl pallet_unique::Config for Runtime {119	type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;120	type CommonWeightInfo = CommonWeights<Self>;121	type RefungibleExtensionsWeightInfo = CommonWeights<Self>;122}123124parameter_types! {125	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);126	pub const MaxCollators: u32 = MAX_COLLATORS;127	pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;128129	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;130}131132#[cfg(not(feature = "session-test-timing"))]133parameter_types! {134	pub const SessionPeriod: BlockNumber = SESSION_LENGTH;135}136137#[cfg(feature = "session-test-timing")]138parameter_types! {139	pub const SessionPeriod: BlockNumber = 3 * MINUTES;140}141142impl pallet_configuration::Config for Runtime {143	type RuntimeEvent = RuntimeEvent;144	type Balance = Balance;145	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;146	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;147	type DefaultCollatorSelectionMaxCollators = MaxCollators;148	type DefaultCollatorSelectionKickThreshold = SessionPeriod;149	type DefaultCollatorSelectionLicenseBond = LicenseBond;150	type MaxXcmAllowedLocations = ConstU32<16>;151	type AppPromotionDailyRate = AppPromotionDailyRate;152	type DayRelayBlocks = DayRelayBlocks;153	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;154}155156impl pallet_maintenance::Config for Runtime {157	type RuntimeEvent = RuntimeEvent;158159	type RuntimeCall = RuntimeCall;160161	#[cfg(feature = "governance")]162	type ManagerOrigin = governance::RootOrTechnicalCommitteeMember;163164	#[cfg(not(feature = "governance"))]165	type ManagerOrigin = frame_system::EnsureRoot<AccountId>;166167	type PreimageOrigin = frame_system::EnsureRoot<AccountId>;168169	#[cfg(feature = "preimage")]170	type Preimages = crate::Preimage;171	#[cfg(not(feature = "preimage"))]172	type Preimages = ();173	type WeightInfo = pallet_maintenance::weights::SubstrateWeight<Self>;174}
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -229,6 +229,7 @@
 preimage = []
 refungible = []
 unique-scheduler = []
+session-test-timing = []
 
 ################################################################################
 # local dependencies
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -218,6 +218,7 @@
 refungible = []
 unique-scheduler = []
 gov-test-timings = []
+session-test-timing = []
 
 ################################################################################
 # local dependencies
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -221,6 +221,7 @@
 refungible = []
 unique-scheduler = []
 gov-test-timings = []
+session-test-timing = []
 
 ################################################################################
 # local dependencies
modifiedtests/src/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth
--- a/tests/src/collator-selection/collatorSelection.seqtest.ts
+++ b/tests/src/collator-selection/collatorSelection.seqtest.ts
@@ -17,36 +17,48 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util';
 
+async function nodeAddress(name: string) {
+  // eslint-disable-next-line require-await
+  return await usingPlaygrounds(async (helper, _) => {
+    const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`;
+
+    const nodeStash = process.env[envNodeStash];
+    if(nodeStash) {
+      return helper.address.normalizeSubstrateToChainFormat(nodeStash);
+    } else {
+      throw Error(`"${envNodeStash}" env var is not set`);
+    }
+  });
+}
+
+async function getInitialInvulnerables() {
+  return await Promise.all([
+    nodeAddress('Alice'),
+    nodeAddress('Bob'),
+    nodeAddress('Charlie'),
+    nodeAddress('Dave'),
+  ]);
+}
+
 async function resetInvulnerables() {
   await usingPlaygrounds(async (helper, privateKey) => {
     const superuser = await privateKey('//Alice');
-    const alice = await privateKey('//Alice');
-    const bob = await privateKey('//Bob');
+    const initialInvulnerables = await getInitialInvulnerables();
+
     const invulnerables = await helper.collatorSelection.getInvulnerables();
-    if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
-      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
-        + 'Current invulnerables\' size: ' + invulnerables.length);
 
-      let nonce = await helper.chain.getNonce(alice.address);
-      // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.
-      if(invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) {
-        await Promise.all([
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),
-        ]);
-      }
+    // Remove all invulnerables but the first one
+    const firstInvulnerable = invulnerables[0];
 
-      nonce = await helper.chain.getNonce(alice.address);
-      await Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-      ]);
+    let nonce = await helper.chain.getNonce(superuser.address);
+    await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++})));
 
-      nonce = await helper.chain.getNonce(alice.address);
-      await Promise.all(invulnerables.map((invulnerable: any) => {
-        if(invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());
-        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
-      }));
+    // Add the initial invulnerables
+    await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++})));
+
+    // Remove the first invulnerable if it's not an initial one
+    if(!initialInvulnerables.includes(firstInvulnerable)) {
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]);
     }
   });
 }
@@ -72,11 +84,11 @@
 
   describe('Dynamic shuffling of collators', () => {
     // These two are the default invulnerables, and should return to be invulnerables after this suite.
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
+    let aliceNode: string;
+    let bobNode: string;
 
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
+    let charlieNode: string;
+    let daveNode: string;
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
@@ -84,99 +96,51 @@
         // Skip the collator block production in dev mode, since the blocks are sealed automatically.
         if(await helper.arrange.isDevNode()) this.skip();
 
-        alice = await privateKey('//Alice');
-        bob = await privateKey('//Bob');
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
-
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
+        [aliceNode, bobNode, charlieNode, daveNode] = await getInitialInvulnerables();
 
         const invulnerables = await helper.collatorSelection.getInvulnerables();
-        if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
-          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
-            + 'Current invulnerables\' size: ' + invulnerables.length);
-
-          let nonce = await helper.chain.getNonce(superuser.address);
-          await Promise.all([
-            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-          ]);
-
-          nonce = await helper.chain.getNonce(superuser.address);
-          await Promise.all(invulnerables.map((invulnerable: any) => {
-            if(invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);
-            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
-          }));
-        }
+        expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4);
+        expect(invulnerables, 'Invalid initial invulnerables').containSubset([aliceNode, bobNode, charlieNode, daveNode]);
       });
     });
 
     itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
       let nonce = await helper.chain.getNonce(superuser.address);
-      await expect(Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),
-      ])).to.be.fulfilled;
 
       nonce = await helper.chain.getNonce(superuser.address);
       await expect(Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [aliceNode], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bobNode], true, {nonce: nonce++}),
       ])).to.be.fulfilled;
 
       const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+      expect(newInvulnerables).to.contain(charlieNode).and.contain(daveNode).and.be.length(2);
 
       await helper.wait.newSessions(2);
 
       const newValidators = await helper.callRpc('api.query.session.validators');
-      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+      expect(newValidators).to.contain(charlieNode).and.contain(daveNode).and.be.length(2);
 
       const lastBlockNumber = await helper.chain.getLatestBlockNumber();
       await helper.wait.newBlocks(1);
-      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();
-      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();
+      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlieNode])).toNumber();
+      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [daveNode])).toNumber();
       expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
     });
 
     after(async () => {
-      await usingPlaygrounds(async (helper) => {
-        if(await helper.arrange.isDevNode()) return;
-
-        let nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all([
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-        ]);
-
-        nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all([
-          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),
-          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),
-        ]);
-      });
+      await resetInvulnerables();
     });
   });
 
   describe('Getting and releasing licenses to collate', () => {
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
     let crowd: IKeyringPair[];
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
         crowd = await helper.arrange.createCrowd(20, 100n, superuser);
 
         // set session keys for everyone
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
         await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
       });
     });
@@ -252,22 +216,13 @@
   });
 
   describe('Onboarding, collating, and offboarding as collator candidates', () => {
-    // These two are the default invulnerables, and should return to be invulnerables after this suite.
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
     let crowd: IKeyringPair[];
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
         crowd = await helper.arrange.createCrowd(20, 100n, superuser);
 
         // set session keys for everyone
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
         await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
       });
     });
@@ -341,10 +296,6 @@
   });
 
   describe('Addition and removal of invulnerables', () => {
-    before(async function() {
-      await resetInvulnerables();
-    });
-
     describe('Positive', () => {
       itSub('Adds an invulnerable', async ({helper}) => {
         const [account] = await helper.arrange.createAccounts([10n], superuser);
@@ -409,7 +360,7 @@
         // 28 non-functioning collators, teehee.
 
         const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
-        const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength;
+        const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength;
         const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
         const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
 
@@ -448,6 +399,10 @@
           .to.be.rejectedWith(/BadOrigin/);
         expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
       });
+
+      after(async function() {
+        await resetInvulnerables();
+      });
     });
   });