fix: add timestamp safe mode (#267)

Following up https://github.com/datahaven-xyz/datahaven/pull/265, we
also need to add Timestamp to the whitelisted Runtime calls.

- [x] Add `RuntimeCall::Timestamp` to `SafeModeWhitelistedCalls`
- [x] Add safe mode test to check it produces blocks

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
This commit is contained in:
Gonza Montiel 2025-11-03 13:00:04 +01:00 committed by GitHub
parent e248a48385
commit 87eb1c1621
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 118 additions and 6 deletions

View file

@ -244,6 +244,8 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
match call {
// Core system calls
RuntimeCall::System(_) => true,
RuntimeCall::Timestamp(_) => true,
RuntimeCall::Randomness(_) => true,
// Safe mode management
RuntimeCall::SafeMode(_) => true,
// Transaction pause management
@ -258,8 +260,6 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
RuntimeCall::Referenda(_) => true,
RuntimeCall::TechnicalCommittee(_) => true,
RuntimeCall::TreasuryCouncil(_) => true,
// Block production - needed to continue producing blocks in safe mode
RuntimeCall::Randomness(_) => true,
_ => false,
}
}

View file

@ -244,6 +244,8 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
match call {
// Core system calls
RuntimeCall::System(_) => true,
RuntimeCall::Timestamp(_) => true,
RuntimeCall::Randomness(_) => true,
// Safe mode management
RuntimeCall::SafeMode(_) => true,
// Transaction pause management
@ -258,8 +260,6 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
RuntimeCall::Referenda(_) => true,
RuntimeCall::TechnicalCommittee(_) => true,
RuntimeCall::TreasuryCouncil(_) => true,
// Block production - needed to continue producing blocks in safe mode
RuntimeCall::Randomness(_) => true,
_ => false,
}
}

View file

@ -244,6 +244,8 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
match call {
// Core system calls
RuntimeCall::System(_) => true,
RuntimeCall::Timestamp(_) => true,
RuntimeCall::Randomness(_) => true,
// Safe mode management
RuntimeCall::SafeMode(_) => true,
// Transaction pause management
@ -258,8 +260,6 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
RuntimeCall::Referenda(_) => true,
RuntimeCall::TechnicalCommittee(_) => true,
RuntimeCall::TreasuryCouncil(_) => true,
// Block production - needed to continue producing blocks in safe mode
RuntimeCall::Randomness(_) => true,
_ => false,
}
}

View file

@ -0,0 +1,112 @@
import { beforeAll, beforeEach, describeSuite, expect } from "@moonwall/cli";
import type { ApiPromise } from "@polkadot/api";
describeSuite({
id: "D010105",
title: "Safe Mode Block Production",
foundationMethods: "dev",
testCases: ({ context, it }) => {
let api: ApiPromise;
beforeAll(async () => {
api = context.polkadotJs();
});
async function getSubstrateBlockNumber(): Promise<number> {
const blockNumber = await api.query.system.number();
return blockNumber.toNumber();
}
beforeEach(async () => {
// Check if safe mode is already active from genesis
let enteredUntil = (await api.query.safeMode.enteredUntil()) as any;
if (!enteredUntil.isSome) {
const enterSafeModeCall = api.tx.safeMode.forceEnter();
const sudoTx = api.tx.sudo.sudo(enterSafeModeCall);
await context.createBlock(sudoTx);
enteredUntil = (await api.query.safeMode.enteredUntil()) as any;
}
expect(enteredUntil.isSome, "Safe mode should be active").to.be.true;
});
it({
id: "T01",
title: "should produce blocks while in safe mode",
test: async () => {
const startBlock = await getSubstrateBlockNumber();
const blocksToCreate = 5;
for (let i = 0; i < blocksToCreate; i++) {
await context.createBlock();
}
const currentBlock = await getSubstrateBlockNumber();
const blocksProduced = currentBlock - startBlock;
expect(blocksProduced).to.be.greaterThanOrEqual(
blocksToCreate,
"Blocks should continue to be produced in safe mode"
);
const exitBlockBefore = await getSubstrateBlockNumber();
const exitSafeModeCall = api.tx.safeMode.forceExit();
const exitSudoTx = api.tx.sudo.sudo(exitSafeModeCall);
await context.createBlock(exitSudoTx);
// Verify the exit block was created (ensures state is updated)
const exitBlockAfter = await getSubstrateBlockNumber();
expect(exitBlockAfter, "Exit block should have been created").to.be.greaterThan(
exitBlockBefore
);
const enteredUntilAfterExit = (await api.query.safeMode.enteredUntil()) as any;
expect(!enteredUntilAfterExit.isSome, "Safe mode should be deactivated").to.be.true;
await context.createBlock();
const finalBlock = await getSubstrateBlockNumber();
expect(finalBlock).to.be.greaterThan(currentBlock);
}
});
it({
id: "T02",
title: "should allow timestamp calls in safe mode",
test: async () => {
const startBlock = await getSubstrateBlockNumber();
// Create a block - this implicitly includes timestamp extrinsic
// The fact that this succeeds proves Timestamp is whitelisted
await context.createBlock();
// Verify the block was created (contains valid timestamp)
const block = await context.viem().getBlock({ blockTag: "latest" });
expect(Number(block.timestamp)).to.be.greaterThan(0);
// Verify block number increased
const currentBlock = await getSubstrateBlockNumber();
expect(currentBlock).to.be.greaterThan(startBlock);
// Exit safe mode
const exitBlockBefore = await getSubstrateBlockNumber();
const exitSafeModeCall = api.tx.safeMode.forceExit();
const exitSudoTx = api.tx.sudo.sudo(exitSafeModeCall);
await context.createBlock(exitSudoTx);
// Verify the exit block was created (ensures state is updated)
const exitBlockAfter = await getSubstrateBlockNumber();
expect(exitBlockAfter, "Exit block should have been created").to.be.greaterThan(
exitBlockBefore
);
// Verify we exited safe mode
const enteredUntilAfterExit = (await api.query.safeMode.enteredUntil()) as any;
expect(!enteredUntilAfterExit.isSome, "Safe mode should be deactivated").to.be.true;
}
});
}
});