From ce216107e42711e1e74adf72178769cc42ef9ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Rodriguez?= Date: Fri, 15 Nov 2024 09:50:31 -0300 Subject: [PATCH] Starlight: add smoke test for BEEFY and MMR digests (#745) * add smoke test for BEEFY digests * fmt * rely on babe's epoch start * fmt --- .../test-consistency-services-payment.ts | 8 +- .../test-beefy-mmr-digests.ts | 77 +++++++++++++++++++ .../test-configuration-consistency.ts | 12 +-- 3 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 test/suites/smoke-test-dancelight/test-beefy-mmr-digests.ts diff --git a/test/suites/smoke-test-common-all/test-consistency-services-payment.ts b/test/suites/smoke-test-common-all/test-consistency-services-payment.ts index ece261233..1fc3288f8 100644 --- a/test/suites/smoke-test-common-all/test-consistency-services-payment.ts +++ b/test/suites/smoke-test-common-all/test-consistency-services-payment.ts @@ -19,7 +19,7 @@ describeSuite({ api = context.polkadotJs(); runtimeVersion = api.runtimeVersion.specVersion.toNumber(); chain = api.consts.system.version.specName.toString(); - blocksPerSession = chain == "dancebox" ? 600n : 50n; + blocksPerSession = chain == "dancebox" || chain == "dancelight" ? 600n : 50n; }); it({ @@ -30,8 +30,10 @@ describeSuite({ return; } const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber(); - - const blockToCheck = Math.trunc(currentBlock / Number(blocksPerSession)) * Number(blocksPerSession); + const blockToCheck = + chain == "dancelight" + ? (await api.query.babe.epochStart()).toJSON()[1] + : Math.trunc(currentBlock / Number(blocksPerSession)) * Number(blocksPerSession); const apiBeforeLatestNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 1)); // If they have collators scheduled, they should have at least enough money to pay diff --git a/test/suites/smoke-test-dancelight/test-beefy-mmr-digests.ts b/test/suites/smoke-test-dancelight/test-beefy-mmr-digests.ts new file mode 100644 index 000000000..048ae0044 --- /dev/null +++ b/test/suites/smoke-test-dancelight/test-beefy-mmr-digests.ts @@ -0,0 +1,77 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; + +import { stringToHex } from "@polkadot/util"; +import { ApiPromise } from "@polkadot/api"; + +describeSuite({ + id: "S19", + title: "Sample suite that only runs on Dancelight chains", + foundationMethods: "read_only", + testCases: ({ it, context }) => { + let api: ApiPromise; + + beforeAll(() => { + api = context.polkadotJs(); + }); + + it({ + id: "C01", + title: "Session change block should update BEEFY and MMR root digests properly", + test: async function () { + const blockToCheck = (await api.query.babe.epochStart()).toJSON()[1]; + + const apiAtBeforeSessionChange = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 5)); + const beefyNextAuthorities = await apiAtBeforeSessionChange.query.beefy.nextAuthorities(); + + const apiAtSessionChange = await api.at(await api.rpc.chain.getBlockHash(blockToCheck)); + + const digestsInSessionChange = (await apiAtSessionChange.query.system.digest()).logs; + const filteredDigests = digestsInSessionChange.filter( + (log) => log.isConsensus === true && log.asConsensus[0].toHex() == stringToHex("BEEF") + ); + + // As session changed, it should contain two BEEFY digests: AuthoritiesChange and MmrRoot. + expect(filteredDigests.length).to.eq(2); + + // 0x01 corresponds to ConsensusLog::AuthoritiesChange enum variant. + expect(filteredDigests[0].asConsensus[1].toHex().startsWith("0x01")).to.be.true; + + // Check if each authority is included in the BEEFY digest + for (const authority of Object.values(beefyNextAuthorities.toJSON())) { + expect(filteredDigests[0].asConsensus[1].toHex().includes(authority.slice(2))).to.be.true; + } + + const firstMmrRootDigest = filteredDigests[1].asConsensus[1].toHex(); + + // 0x03 corresponds to ConsensusLog::MmrRoot enum variant. + expect(firstMmrRootDigest.startsWith("0x03")).to.be.true; + + // Second BEEF log should contain the MMR root. + // Length should be 68 (0x03 + 32 bytes MMR root). + expect(firstMmrRootDigest.length).to.eq(68); + + // Now let's check just after session change + const apiAtAfterSessionChange = await api.at(await api.rpc.chain.getBlockHash(blockToCheck + 1)); + const digestsAfterSessionChange = (await apiAtAfterSessionChange.query.system.digest()).logs; + const filteredDigestsAfterSessionChange = digestsAfterSessionChange.filter( + (log) => log.isConsensus === true && log.asConsensus[0].toHex() == stringToHex("BEEF") + ); + + // Now we should only have the MmrRoot BEEFY digest (as session didn't change yet). + expect(filteredDigestsAfterSessionChange.length).to.eq(1); + + const secondMmrRootDigest = filteredDigestsAfterSessionChange[0].asConsensus[1].toHex(); + + // New MmrRoot digest should be different than the first one found. + expect(secondMmrRootDigest).to.not.eq(firstMmrRootDigest); + + // 0x03 corresponds to ConsensusLog::MmrRoot enum variant. + expect(secondMmrRootDigest.startsWith("0x03")).to.be.true; + + // Second BEEF log should contain the MMR root. + // Length should be 68 (0x03 + 32 bytes MMR root). + expect(secondMmrRootDigest.length).to.eq(68); + }, + }); + }, +}); diff --git a/test/suites/smoke-test-dancelight/test-configuration-consistency.ts b/test/suites/smoke-test-dancelight/test-configuration-consistency.ts index 30598dc3d..1f22baff2 100644 --- a/test/suites/smoke-test-dancelight/test-configuration-consistency.ts +++ b/test/suites/smoke-test-dancelight/test-configuration-consistency.ts @@ -10,28 +10,24 @@ describeSuite({ foundationMethods: "read_only", testCases: ({ it, context }) => { let api: ApiPromise; - let blocksPerSession; + const blocksPerSession = 600n; const costPerSession = 100_000_000n; const costPerBlock = 1_000_000n; beforeAll(() => { api = context.polkadotJs(); - const chain = api.consts.system.version.specName.toString(); - blocksPerSession = chain == "dancebox" ? 600n : 50n; }); it({ id: "C01", title: "Config for registered paras should be consistent", test: async function () { - const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber(); + const sessionIndex = (await api.query.session.currentIndex()).toNumber(); + const blockToCheck = (await api.query.babe.epochStart()).toJSON()[1]; - const blockToCheck = Math.trunc(currentBlock / Number(blocksPerSession)) * Number(blocksPerSession); const apiBeforeLatestNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 1)); - const config = await api.query.collatorConfiguration.activeConfig(); - // get current session - const sessionIndex = (await api.query.session.currentIndex()).toNumber(); + // get pending authorities // the reason for getting pending is that the hasEnoughCredits check it's done over the pending ones const pendingAuthorityAssignment = (