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

difftreelog

Merge pull request #988 from UniqueNetwork/fix/collector-selection-tests-release-60

Yaroslav Bolyukin2023-09-08parents: #e6eb884 #095843d.patch.diff
in: master

9 files changed

modified.baedeker/collator-selection.jsonnetdiffbeforeafterboth
31 bin: $.bin,31 bin: $.bin,
32 wantedKeys: 'para',32 wantedKeys: 'para',
33 },33 },
34 for name in ['alice', 'bob']34 for name in ['alpha', 'beta', 'gamma', 'delta']
35 },35 },
36};36};
3737
modified.docker/docker-compose.gov.j2diffbeforeafterboth
21 options:21 options:
22 max-size: "1m"22 max-size: "1m"
23 max-file: "3"23 max-file: "3"
24 command: cargo run --release --features={{ NETWORK }}-runtime,{{ WASM_NAME }}-runtime/gov-test-timings -- --dev -linfo --rpc-cors=all --unsafe-rpc-external24 command: cargo run --release --features={{ NETWORK }}-runtime,gov-test-timings -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
2525
modified.github/workflows/collator-selection.ymldiffbeforeafterboth
138 tag: ${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }}138 tag: ${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }}
139 context: .139 context: .
140 dockerfile: .docker/Dockerfile-unique140 dockerfile: .docker/Dockerfile-unique
141 args: |141 args: |
142 --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }}142 --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }},session-test-timings
143 --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}143 --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
144 dockerhub_username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}144 dockerhub_username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}
145 dockerhub_token: ${{ secrets.CORE_DOCKERHUB_TOKEN }}145 dockerhub_token: ${{ secrets.CORE_DOCKERHUB_TOKEN }}
146146
modifiednode/cli/Cargo.tomldiffbeforeafterboth
108runtime-benchmarks = ['opal-runtime/runtime-benchmarks', 'polkadot-cli/runtime-benchmarks', 'polkadot-service/runtime-benchmarks', 'quartz-runtime?/runtime-benchmarks', 'sc-service/runtime-benchmarks', 'unique-runtime?/runtime-benchmarks']108runtime-benchmarks = ['opal-runtime/runtime-benchmarks', 'polkadot-cli/runtime-benchmarks', 'polkadot-service/runtime-benchmarks', 'quartz-runtime?/runtime-benchmarks', 'sc-service/runtime-benchmarks', 'unique-runtime?/runtime-benchmarks']
109sapphire-runtime = ['quartz-runtime', 'quartz-runtime/become-sapphire']109sapphire-runtime = ['quartz-runtime', 'quartz-runtime/become-sapphire']
110try-runtime = ['opal-runtime?/try-runtime', 'quartz-runtime?/try-runtime', 'try-runtime-cli/try-runtime', 'unique-runtime?/try-runtime']110try-runtime = ['opal-runtime?/try-runtime', 'quartz-runtime?/try-runtime', 'try-runtime-cli/try-runtime', 'unique-runtime?/try-runtime']
111session-test-timings = ['opal-runtime/session-test-timings', 'quartz-runtime?/session-test-timings', 'unique-runtime?/session-test-timings']
112gov-test-timings = ['opal-runtime/gov-test-timings', 'quartz-runtime?/gov-test-timings', 'unique-runtime?/gov-test-timings']
111113
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
125 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);125 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
126 pub const MaxCollators: u32 = MAX_COLLATORS;126 pub const MaxCollators: u32 = MAX_COLLATORS;
127 pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;127 pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;
128 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;128
129 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;129 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;
130}130}
131
132#[cfg(not(feature = "session-test-timings"))]
133parameter_types! {
134 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;
135}
136
137#[cfg(feature = "session-test-timings")]
138parameter_types! {
139 pub const SessionPeriod: BlockNumber = 5 * MINUTES;
140}
131141
132impl pallet_configuration::Config for Runtime {142impl pallet_configuration::Config for Runtime {
133 type RuntimeEvent = RuntimeEvent;143 type RuntimeEvent = RuntimeEvent;
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
229preimage = []229preimage = []
230refungible = []230refungible = []
231unique-scheduler = []231unique-scheduler = []
232session-test-timings = []
232233
233################################################################################234################################################################################
234# local dependencies235# local dependencies
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
218refungible = []218refungible = []
219unique-scheduler = []219unique-scheduler = []
220gov-test-timings = []220gov-test-timings = []
221session-test-timings = []
221222
222################################################################################223################################################################################
223# local dependencies224# local dependencies
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
221refungible = []221refungible = []
222unique-scheduler = []222unique-scheduler = []
223gov-test-timings = []223gov-test-timings = []
224session-test-timings = []
224225
225################################################################################226################################################################################
226# local dependencies227# local dependencies
modifiedtests/src/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util';
19
20async function nodeAddress(name: string) {
21 // eslint-disable-next-line require-await
22 return await usingPlaygrounds(async (helper, _) => {
23 const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`;
24
25 const nodeStash = process.env[envNodeStash];
26 if(nodeStash) {
27 return helper.address.normalizeSubstrateToChainFormat(nodeStash);
28 } else {
29 throw Error(`"${envNodeStash}" env var is not set`);
30 }
31 });
32}
33
34async function getInitialInvulnerables() {
35 return await Promise.all([
36 nodeAddress('alpha'),
37 nodeAddress('beta'),
38 nodeAddress('gamma'),
39 nodeAddress('delta'),
40 ]);
41}
1942
20async function resetInvulnerables() {43async function resetInvulnerables() {
21 await usingPlaygrounds(async (helper, privateKey) => {44 await usingPlaygrounds(async (helper, privateKey) => {
22 const superuser = await privateKey('//Alice');45 const superuser = await privateKey('//Alice');
23 const alice = await privateKey('//Alice');46 const initialInvulnerables = await getInitialInvulnerables();
24 const bob = await privateKey('//Bob');47
25 const invulnerables = await helper.collatorSelection.getInvulnerables();48 const invulnerables = await helper.collatorSelection.getInvulnerables();
26 if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {49
27 console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '50 // Remove all invulnerables but the first one
28 + 'Current invulnerables\' size: ' + invulnerables.length);51 const firstInvulnerable = invulnerables[0];
2952
30 let nonce = await helper.chain.getNonce(alice.address);53 let nonce = await helper.chain.getNonce(superuser.address);
31 // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.
32 if(invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) {
33 await Promise.all([54 await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++})));
34 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),55
35 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),56 // Add the initial invulnerables
36 ]);
37 }
38
39 nonce = await helper.chain.getNonce(alice.address);
40 await Promise.all([57 await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++})));
41 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),58
42 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),59 // Remove the first invulnerable if it's not an initial one
43 ]);
44
45 nonce = await helper.chain.getNonce(alice.address);
46 await Promise.all(invulnerables.map((invulnerable: any) => {
47 if(invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());60 if(!initialInvulnerables.includes(firstInvulnerable)) {
48 return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});61 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]);
49 }));62 }
50 }
51 });63 });
52}64}
5365
7284
73 describe('Dynamic shuffling of collators', () => {85 describe('Dynamic shuffling of collators', () => {
74 // These two are the default invulnerables, and should return to be invulnerables after this suite.86 // These two are the default invulnerables, and should return to be invulnerables after this suite.
75 let alice: IKeyringPair;87 let alphaNode: string;
76 let bob: IKeyringPair;88 let betaNode: string;
7789
78 let charlie: IKeyringPair;90 let gammaNode: string;
79 let dave: IKeyringPair;91 let deltaNode: string;
8092
81 before(async function() {93 before(async function() {
82 await usingPlaygrounds(async (helper, privateKey) => {94 await usingPlaygrounds(async (helper, privateKey) => {
83 // todo:collator see again if blocks start to be finalized in dev mode95 // todo:collator see again if blocks start to be finalized in dev mode
84 // Skip the collator block production in dev mode, since the blocks are sealed automatically.96 // Skip the collator block production in dev mode, since the blocks are sealed automatically.
85 if(await helper.arrange.isDevNode()) this.skip();97 if(await helper.arrange.isDevNode()) this.skip();
8698
87 alice = await privateKey('//Alice');99 [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables();
88 bob = await privateKey('//Bob');
89 charlie = await privateKey('//Charlie');
90 dave = await privateKey('//Dave');
91100
92 expect((await helper.session.setOwnKeysFromAddress(charlie))101 const invulnerables = await helper.collatorSelection.getInvulnerables();
93 .status.toLowerCase()).to.be.equal('success');
94 expect((await helper.session.setOwnKeysFromAddress(dave))102 expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4);
95 .status.toLowerCase()).to.be.equal('success');
96
97 const invulnerables = await helper.collatorSelection.getInvulnerables();
98 if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
99 console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '103 expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]);
100 + 'Current invulnerables\' size: ' + invulnerables.length);
101
102 let nonce = await helper.chain.getNonce(superuser.address);
103 await Promise.all([
104 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
105 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
106 ]);
107
108 nonce = await helper.chain.getNonce(superuser.address);
109 await Promise.all(invulnerables.map((invulnerable: any) => {
110 if(invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);
111 return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
112 }));
113 }
114 });104 });
115 });105 });
116106
117 itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {107 itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
118 let nonce = await helper.chain.getNonce(superuser.address);108 let nonce = await helper.chain.getNonce(superuser.address);
119 await expect(Promise.all([
120 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),
121 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),
122 ])).to.be.fulfilled;
123109
124 nonce = await helper.chain.getNonce(superuser.address);110 nonce = await helper.chain.getNonce(superuser.address);
125 await expect(Promise.all([111 await expect(Promise.all([
126 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),112 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}),
127 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),113 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}),
128 ])).to.be.fulfilled;114 ])).to.be.fulfilled;
129115
130 const newInvulnerables = await helper.collatorSelection.getInvulnerables();116 const newInvulnerables = await helper.collatorSelection.getInvulnerables();
131 expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);117 expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
132118
133 await helper.wait.newSessions(2);119 await helper.wait.newSessions(2);
134120
135 const newValidators = await helper.callRpc('api.query.session.validators');121 const newValidators = await helper.callRpc('api.query.session.validators');
136 expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);122 expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
137123
138 const lastBlockNumber = await helper.chain.getLatestBlockNumber();124 const lastBlockNumber = await helper.chain.getLatestBlockNumber();
139 await helper.wait.newBlocks(1);125 await helper.wait.newBlocks(1);
140 const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();126 const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber();
141 const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();127 const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber();
142 expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;128 expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true;
143 });129 });
144130
145 after(async () => {131 after(async () => {
146 await usingPlaygrounds(async (helper) => {132 await resetInvulnerables();
147 if(await helper.arrange.isDevNode()) return;
148
149 let nonce = await helper.chain.getNonce(superuser.address);
150 await Promise.all([
151 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
152 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
153 ]);
154
155 nonce = await helper.chain.getNonce(superuser.address);
156 await Promise.all([
157 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),
158 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),
159 ]);
160 });
161 });133 });
162 });134 });
163135
164 describe('Getting and releasing licenses to collate', () => {136 describe('Getting and releasing licenses to collate', () => {
165 let charlie: IKeyringPair;
166 let dave: IKeyringPair;
167 let crowd: IKeyringPair[];137 let crowd: IKeyringPair[];
168138
169 before(async function() {139 before(async function() {
170 await usingPlaygrounds(async (helper, privateKey) => {140 await usingPlaygrounds(async (helper, privateKey) => {
171 charlie = await privateKey('//Charlie');
172 dave = await privateKey('//Dave');
173 crowd = await helper.arrange.createCrowd(20, 100n, superuser);141 crowd = await helper.arrange.createCrowd(20, 100n, superuser);
174142
175 // set session keys for everyone143 // set session keys for everyone
176 expect((await helper.session.setOwnKeysFromAddress(charlie))
177 .status.toLowerCase()).to.be.equal('success');
178 expect((await helper.session.setOwnKeysFromAddress(dave))
179 .status.toLowerCase()).to.be.equal('success');
180 await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));144 await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
181 });145 });
182 });146 });
252 });216 });
253217
254 describe('Onboarding, collating, and offboarding as collator candidates', () => {218 describe('Onboarding, collating, and offboarding as collator candidates', () => {
255 // These two are the default invulnerables, and should return to be invulnerables after this suite.
256 let charlie: IKeyringPair;
257 let dave: IKeyringPair;
258 let crowd: IKeyringPair[];219 let crowd: IKeyringPair[];
259220
260 before(async function() {221 before(async function() {
261 await usingPlaygrounds(async (helper, privateKey) => {222 await usingPlaygrounds(async (helper, privateKey) => {
262 charlie = await privateKey('//Charlie');
263 dave = await privateKey('//Dave');
264 crowd = await helper.arrange.createCrowd(20, 100n, superuser);223 crowd = await helper.arrange.createCrowd(20, 100n, superuser);
265224
266 // set session keys for everyone225 // set session keys for everyone
267 expect((await helper.session.setOwnKeysFromAddress(charlie))
268 .status.toLowerCase()).to.be.equal('success');
269 expect((await helper.session.setOwnKeysFromAddress(dave))
270 .status.toLowerCase()).to.be.equal('success');
271 await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));226 await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
272 });227 });
273 });228 });
341 });296 });
342297
343 describe('Addition and removal of invulnerables', () => {298 describe('Addition and removal of invulnerables', () => {
344 before(async function() {
345 await resetInvulnerables();
346 });
347
348 describe('Positive', () => {299 describe('Positive', () => {
349 itSub('Adds an invulnerable', async ({helper}) => {300 itSub('Adds an invulnerable', async ({helper}) => {
409 // 28 non-functioning collators, teehee.360 // 28 non-functioning collators, teehee.
410361
411 const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;362 const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
412 const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength;363 const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength;
413 const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);364 const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
414 const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);365 const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
415366
449 expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);400 expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
450 });401 });
402
403 after(async function() {
404 await resetInvulnerables();
405 });
451 });406 });
452 });407 });
453408