1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';1920async function resetInvulnerables() {21 await usingPlaygrounds(async (helper, privateKey) => {22 const superuser = await privateKey('//Alice');23 const alice = await privateKey('//Alice');24 const bob = await privateKey('//Bob');25 const invulnerables = await helper.collatorSelection.getInvulnerables();26 if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {27 console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 28 + 'Current invulnerables\' size: ' + invulnerables.length);29 30 let nonce = await helper.chain.getNonce(alice.address);31 await Promise.all([32 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),33 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),34 ]);3536 nonce = await helper.chain.getNonce(alice.address);37 await Promise.all(invulnerables.map((invulnerable: any) => {38 if (invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());39 return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});40 }));41 }42 });43}44454647describe('Integration Test: Collator Selection', () => {48 let superuser: IKeyringPair;4950 51 let alice: IKeyringPair;52 let bob: IKeyringPair;5354 let charlie: IKeyringPair;55 let dave: IKeyringPair;56 5758 before(async function() { 59 await usingPlaygrounds(async (helper, privateKey) => {60 requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);6162 63 64 65 alice = await privateKey('//Alice');66 bob = await privateKey('//Bob');67 charlie = await privateKey('//Charlie');68 dave = await privateKey('//Dave');6970 superuser = await privateKey('//Alice');71 });72 });7374 describe('Dynamic shuffling of collators', () => {75 before(async function() { 76 await usingPlaygrounds(async (helper) => {77 expect((await helper.collatorSelection.setOwnKeys(charlie))78 .status.toLowerCase()).to.be.equal('success');79 expect((await helper.collatorSelection.setOwnKeys(dave))80 .status.toLowerCase()).to.be.equal('success');81 82 83 84 const invulnerables = await helper.callRpc('api.query.collatorSelection.invulnerables');85 if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {86 console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 87 + 'Current invulnerables\' size: ' + invulnerables.length);88 89 await Promise.all([90 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: 0}),91 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: 1}),92 ]);93 94 let nonce = 0;95 await Promise.all(invulnerables.map((invulnerable: any) => {96 if (invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);97 return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});98 }));99 }100 });101 });102 103 itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {104 await expect(Promise.all([105 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: 0}),106 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: 1}),107 ])).to.be.fulfilled;108 109 await expect(Promise.all([110 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: 0}),111 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: 1}),112 ])).to.be.fulfilled;113 114 const newInvulnerables = await helper.callRpc('api.query.collatorSelection.invulnerables');115 expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);116 117 const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;118 let currentSessionIndex = -1;119 console.log('Waiting for the session after the next.' 120 + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');121 122 while (currentSessionIndex < expectedSessionIndex) {123 124 currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {125 126 console.log('starting wait...');127 console.time('ein');128 await helper.wait.newBlocks(1);129 console.timeLog('ein');130 const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();131 console.timeEnd('ein');132 resolve(res);133 }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;134 }135 136 const newValidators = await helper.callRpc('api.query.session.validators');137 expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);138 139 const lastBlockNumber = await helper.chain.getLatestBlockNumber();140 await helper.wait.newBlocks(1);141 const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();142 const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();143 expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;144 });145 146 147 148 149 after(async () => {150 await usingPlaygrounds(async (helper) => {151 if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;152153 await Promise.all([154 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: 0}),155 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: 1}),156 ]);157 158 await Promise.all([159 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: 0}),160 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: 1}),161 ]);162 });163 });164 });165166 167 168169 describe('Addition and removal of invulnerables', () => {170 before(async function() {171 await resetInvulnerables();172 });173174 describe('Positive', () => {175 itSub('Adds an invulnerable', async ({helper}) => {176 const [account] = await helper.arrange.createAccounts([10n], superuser);177 const invulnerables = await helper.collatorSelection.getInvulnerables();178179 await helper.collatorSelection.setOwnKeys(account);180 await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);181 182 const newInvulnerables = await helper.collatorSelection.getInvulnerables();183 expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);184 });185186 itSub('Removes an invulnerable', async ({helper}) => {187 const invulnerables = await helper.collatorSelection.getInvulnerables();188 const lastInvulnerable = invulnerables.pop();189190 await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);191 const newInvulnerables = await helper.collatorSelection.getInvulnerables();192 193 expect(newInvulnerables).to.have.all.members(invulnerables);194 });195 });196197 describe('Negative', () => {198 itSub('Does not duplicate an invulnerable', async ({helper}) => {199 const invulnerables = await helper.collatorSelection.getInvulnerables();200 201 await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))202 .to.be.fulfilled;203 const newInvulnerables = await helper.collatorSelection.getInvulnerables();204 expect(newInvulnerables).to.have.all.members(invulnerables);205 });206207 itSub('Cannot allow invulnerables to be empty', async ({helper}) => {208 const invulnerables = await helper.collatorSelection.getInvulnerables();209 const lastInvulnerable = invulnerables.pop();210211 let nonce = await helper.chain.getNonce(superuser.address);212 await Promise.all(invulnerables.map((i: any) => 213 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));214215 await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))216 .to.be.rejected;217218 const newInvulnerables = await helper.collatorSelection.getInvulnerables();219 expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);220 221 222 nonce = await helper.chain.getNonce(superuser.address);223 await Promise.all(invulnerables.map((i: any) => 224 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));225 });226227 itSub('Cannot have too many invulnerables', async ({helper}) => {228 const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;229 const invulnerablesUntilLimit = 30 - invulnerablesLength;230 const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);231 const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);232233 await Promise.all(newInvulnerables.map((i: IKeyringPair) => 234 helper.collatorSelection.setOwnKeys(i)));235 await helper.collatorSelection.setOwnKeys(lastInvulnerable);236237 let nonce = await helper.chain.getNonce(superuser.address);238 await Promise.all(newInvulnerables.map((i: IKeyringPair) => 239 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));240241 await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))242 .to.be.rejected; 243 244 245 nonce = await helper.chain.getNonce(superuser.address);246 await Promise.all(newInvulnerables.map((i: IKeyringPair) => 247 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));248 });249250 itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {251 const [account] = await helper.arrange.createAccounts([10n], bob);252 const invulnerables = await helper.collatorSelection.getInvulnerables();253254 await helper.collatorSelection.setOwnKeys(account);255 await expect(helper.collatorSelection.addInvulnerable(bob, account.address))256 .to.be.rejectedWith(/BadOrigin/);257258 const newInvulnerables = await helper.collatorSelection.getInvulnerables();259 expect(newInvulnerables).to.be.members(invulnerables);260 });261262 itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {263 const invulnerables = await helper.collatorSelection.getInvulnerables();264 await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))265 .to.be.rejectedWith(/BadOrigin/);266 expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);267 });268 });269 270 271 });272});