--- a/runtime/common/config/substrate.rs +++ b/runtime/common/config/substrate.rs @@ -35,7 +35,6 @@ runtime_common::DealWithFees, Runtime, Event, Call, Origin, PalletInfo, System, Balances, Treasury, SS58Prefix, Aura, Session, SessionKeys, CollatorSelection, Version, }; -use xcm::v1::BodyId; use up_common::{types::*, constants::*}; parameter_types! { @@ -218,9 +217,8 @@ } parameter_types! { - pub const Period: u32 = 6 * HOURS; - pub const Offset: u32 = 0; - //pub const MaxAuthorities: u32 = 100_000; + pub const SessionPeriod: BlockNumber = 6 * HOURS; + pub const SessionOffset: BlockNumber = 0; } impl pallet_session::Config for Runtime { @@ -228,8 +226,8 @@ type ValidatorId = ::AccountId; // we don't have stash and controller, thus we don't need the convert as well. type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ShouldEndSession = pallet_session::PeriodicSessions; - type NextSessionRotation = pallet_session::PeriodicSessions; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; type SessionManager = CollatorSelection; // Essentially just Aura, but lets be pedantic. type SessionHandler = ::KeyTypeIdProviders; @@ -252,24 +250,20 @@ pub const PotId: PalletId = PalletId(*b"PotStake"); pub const MaxCandidates: u32 = 1000; pub const MinCandidates: u32 = 5; - pub const SessionLength: BlockNumber = 6 * HOURS; pub const MaxInvulnerables: u32 = 100; - pub const ExecutiveBody: BodyId = BodyId::Executive; } - -// We allow root only to execute privileged collator selection operations. -pub type CollatorSelectionUpdateOrigin = EnsureRoot; impl pallet_collator_selection::Config for Runtime { type Event = Event; type Currency = Balances; - type UpdateOrigin = CollatorSelectionUpdateOrigin; + // We allow root only to execute privileged collator selection operations. + type UpdateOrigin = EnsureRoot; type PotId = PotId; type MaxCandidates = MaxCandidates; type MinCandidates = MinCandidates; type MaxInvulnerables = MaxInvulnerables; - // should be a multiple of session or things will get inconsistent - type KickThreshold = Period; + // Should be a multiple of session or things will get inconsistent. + type KickThreshold = SessionPeriod; type ValidatorId = ::AccountId; type ValidatorIdOf = pallet_collator_selection::IdentityCollator; type ValidatorRegistration = Session; --- a/tests/package.json +++ b/tests/package.json @@ -75,7 +75,7 @@ "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts", "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts", "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts", - "testShuffleCollators": "mocha --timeout 9999999 -r ts-node/register ./**/shuffleCollators.test.ts", + "testCollatorSelection": "mocha --timeout 9999999 -r ts-node/register ./**/collatorSelection.test.ts", "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts", "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts", "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", --- /dev/null +++ b/tests/src/collatorSelection.test.ts @@ -0,0 +1,108 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {IKeyringPair} from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; +import usingApi, {submitTransactionAsync} from './substrate/substrate-api'; +import waitNewBlocks from './substrate/wait-new-blocks'; +import {expect} from 'chai'; +import {getBlockNumber, getGenericResult} from './util/helpers'; + +let alice: IKeyringPair; +let bob: IKeyringPair; +let charlie: IKeyringPair; +let dave: IKeyringPair; +//let eve: IKeyringPair; + +// todo Most preferable to launch this test in parallel somehow -- or change the session period (6 hrs) for Opal specifically. +describe('Integration Test: Dynamic shuffling of collators', () => { + before(async () => { + await usingApi(async api => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + charlie = privateKey('//Charlie'); + dave = privateKey('//Dave'); + //eve = privateKey('//Eve'); + + const txC = api.tx.session.setKeys( + '0x' + Buffer.from(charlie.addressRaw).toString('hex'), + '0x0', + ); + const eventsC = await submitTransactionAsync(charlie, txC); + const resultC = getGenericResult(eventsC); + expect(resultC.success).to.be.true; + + const txD = api.tx.session.setKeys( + '0x' + Buffer.from(dave.addressRaw).toString('hex'), + '0x0', + ); + const eventsD = await submitTransactionAsync(dave, txD); + const resultD = getGenericResult(eventsD); + expect(resultD.success).to.be.true; + + const validators = (await api.query.session.validators()).toJSON(); + expect(validators).to.contain(alice.address).and.contain(bob.address).and.be.length(2); + }); + }); + + it('Change invulnerables and make sure they start producing blocks', async () => { + await usingApi(async (api) => { + const tx = api.tx.collatorSelection.setInvulnerables([ + charlie.address, + dave.address, + ]); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await submitTransactionAsync(alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + const newInvulnerables = (await api.query.collatorSelection.invulnerables()).toJSON(); + expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2); + + const expectedSessionIndex = (await api.query.session.currentIndex() as any).toNumber() + 2; + let currentSessionIndex = -1; + console.log('Waiting for the session after the next.' + + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.'); + while (currentSessionIndex < expectedSessionIndex) { + await waitNewBlocks(api, 1); + currentSessionIndex = (await api.query.session.currentIndex() as any).toNumber(); + // todo implement a timeout in case new blocks are not being produced? session length needed + } + + const newValidators = (await api.query.session.validators()).toJSON(); + expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2); + + const lastBlockNumber = await getBlockNumber(api); + await waitNewBlocks(api, 1); + const lastCharlieBlock = (await api.query.collatorSelection.lastAuthoredBlock(charlie.address) as any).toNumber(); + const lastDaveBlock = (await api.query.collatorSelection.lastAuthoredBlock(dave.address) as any).toNumber(); + expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true; + }); + }); + + after(async () => { + await usingApi(async (api) => { + const tx = api.tx.collatorSelection.setInvulnerables([ + alice.address, + bob.address, + ]); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await submitTransactionAsync(alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }); + }); +}); --- a/tests/src/shuffleCollators.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {IKeyringPair} from '@polkadot/types/types'; -import privateKey from './substrate/privateKey'; -import usingApi, {submitTransactionAsync} from './substrate/substrate-api'; -import waitNewBlocks from './substrate/wait-new-blocks'; -import {expect} from 'chai'; -import {getBlockNumber, getGenericResult} from './util/helpers'; - -let alice: IKeyringPair; -let bob: IKeyringPair; -let charlie: IKeyringPair; -let dave: IKeyringPair; -//let eve: IKeyringPair; - -describe('Integration Test: Dynamic shuffling of collators', () => { - before(async () => { - await usingApi(async () => { - alice = privateKey('//Alice'); - bob = privateKey('//Bob'); - charlie = privateKey('//Charlie'); - dave = privateKey('//Dave'); - //eve = privateKey('//Eve'); - }); - }); - - it('Change invulnerables and make sure they start producing blocks', async () => { - await usingApi(async (api) => { - const txC = api.tx.session.setKeys( - '0x' + Buffer.from(charlie.addressRaw).toString('hex'), - '0x0', - ); - const eventsC = await submitTransactionAsync(charlie, txC); - const resultC = getGenericResult(eventsC); - expect(resultC.success).to.be.true; - - const txD = api.tx.session.setKeys( - '0x' + Buffer.from(dave.addressRaw).toString('hex'), - '0x0', - ); - const eventsD = await submitTransactionAsync(dave, txD); - const resultD = getGenericResult(eventsD); - expect(resultD.success).to.be.true; - - const tx = api.tx.collatorSelection.setInvulnerables([ - charlie.address, - dave.address, - ]); - const sudoTx = api.tx.sudo.sudo(tx as any); - const events = await submitTransactionAsync(alice, sudoTx); - const result = getGenericResult(events); - expect(result.success).to.be.true; - - const newInvulnerables = (await api.query.collatorSelection.invulnerables()).toJSON(); - expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address); - expect(newInvulnerables).to.be.length(2); - - const expectedSessionIndex = (await api.query.session.currentIndex() as any).toNumber() + 2; - let currentSessionIndex = -1; - console.log('Waiting for the session after the next. This might take a while...'); - while (currentSessionIndex < expectedSessionIndex) { - await waitNewBlocks(api, 1); - currentSessionIndex = (await api.query.session.currentIndex() as any).toNumber(); - // todo implement a timeout in case new blocks are not being produced? session length needed - } - - const newValidators = (await api.query.session.validators()).toJSON(); - expect(newValidators).to.contain(charlie.address).and.contain(dave.address); - expect(newValidators).to.be.length(2); - - const lastBlockNumber = await getBlockNumber(api); - await waitNewBlocks(api, 1); - const lastCharlieBlock = (await api.query.collatorSelection.lastAuthoredBlock(charlie.address) as any).toNumber(); - const lastDaveBlock = (await api.query.collatorSelection.lastAuthoredBlock(dave.address) as any).toNumber(); - expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true; - }); - }); - - after(async () => { - await usingApi(async (api) => { - const tx = api.tx.collatorSelection.setInvulnerables([ - alice.address, - bob.address, - ]); - const sudoTx = api.tx.sudo.sudo(tx as any); - const events = await submitTransactionAsync(alice, sudoTx); - const result = getGenericResult(events); - expect(result.success).to.be.true; - }); - }); -});