diff --git a/test/suites/smoke-test/test-randomness-consistency.ts b/test/suites/smoke-test/test-randomness-consistency.ts new file mode 100644 index 000000000..10e1ebda5 --- /dev/null +++ b/test/suites/smoke-test/test-randomness-consistency.ts @@ -0,0 +1,74 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; + +import { ApiPromise } from "@polkadot/api"; +import { fetchRandomnessEvent } from "util/block"; +describeSuite({ + id: "S09", + title: "Sample suite that only runs on Dancebox chains", + foundationMethods: "read_only", + testCases: ({ it, context }) => { + let api: ApiPromise; + + beforeAll(() => { + api = context.polkadotJs(); + }); + + it({ + id: "C01", + title: "Randomness storage is empty because on-finalize cleans it, unless on session change boundaries", + test: async function () { + const sessionLength = 300; + const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber(); + const randomness = await api.query.collatorAssignment.randomness(); + + // if the next block is a session change, then this storage will be populated + if (currentBlock + (1 % sessionLength) == 0) { + expect(randomness.isEmpty).to.not.be.true; + } else { + expect(randomness.isEmpty).to.be.true; + } + }, + }); + + it({ + id: "C02", + title: "Rotation happened at previous session boundary", + test: async function () { + const sessionLength = 300; + const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber(); + + const blockToCheck = Math.trunc(currentBlock / sessionLength) * sessionLength; + const apiAtIssuanceNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck)); + const apiAtIssuanceBeforeNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 1)); + + // Just before, the randomness was not empty + const randomnessBeforeSession = + await apiAtIssuanceBeforeNewSession.query.collatorAssignment.randomness(); + expect(randomnessBeforeSession.isEmpty).to.not.be.true; + + // After, the randomness gets cleaned + const randomnessAfterSession = await apiAtIssuanceNewSession.query.collatorAssignment.randomness(); + expect(randomnessAfterSession.isEmpty).to.be.true; + + // The rotation event should have kicked in, if enabled + const events = await apiAtIssuanceNewSession.query.system.events(); + const randomnessEvent = fetchRandomnessEvent(events); + const session = await apiAtIssuanceNewSession.query.session.currentIndex(); + + expect(randomnessEvent.randomSeed.toHex()).to.not.be.equal( + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + expect(randomnessEvent.targetSession.toNumber()).to.be.equal(session.toNumber() + 1); + const configuration = await apiAtIssuanceNewSession.query.configuration.activeConfig(); + if ( + configuration.fullRotationPeriod == 0 || + session.toNumber() % configuration.fullRotationPeriod == 0 + ) { + expect(randomnessEvent.fullRotation.toHuman()).to.be.false; + } else { + expect(randomnessEvent.fullRotation.toHuman()).to.be.true; + } + }, + }); + }, +}); diff --git a/test/util/block.ts b/test/util/block.ts index ab26d448e..0d9ecbd46 100644 --- a/test/util/block.ts +++ b/test/util/block.ts @@ -3,7 +3,7 @@ import { filterAndApply } from "@moonwall/util"; import { ApiPromise } from "@polkadot/api"; import { AccountId32, EventRecord } from "@polkadot/types/interfaces"; - +import { Vec, u8, u32, bool } from "@polkadot/types-codec"; export async function jumpSessions(context: DevModeContext, count: number): Promise { const session = (await context.polkadotJs().query.session.currentIndex()).addn(count.valueOf()).toNumber(); @@ -151,6 +151,18 @@ export function fetchRewardAuthorContainers(events: EventRecord[] = []) { return filtered; } +export function fetchRandomnessEvent(events: EventRecord[] = []) { + const filtered = filterAndApply( + events, + "collatorAssignment", + ["NewPendingAssignment"], + ({ event }: EventRecord) => + event.data as unknown as { randomSeed: Vec; fullRotation: bool; targetSession: u32 } + ); + + return filtered[0]; +} + export function fetchIssuance(events: EventRecord[] = []) { const filtered = filterAndApply( events,