Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 38 additions & 13 deletions packages/assertion-monitor/__test__/monitoring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
NO_CONFIRMATION_EVENTS_ALERT,
CONFIRMATION_DELAY_ALERT,
CREATION_EVENT_STUCK_ALERT,
NON_BOLD_NO_RECENT_CREATION_ALERT,
VALIDATOR_WHITELIST_DISABLED_ALERT,
NO_CONFIRMATION_BLOCKS_WITH_CONFIRMATION_EVENTS_ALERT,
BOLD_LOW_BASE_STAKE_ALERT,
Expand Down Expand Up @@ -92,7 +91,8 @@ describe('Assertion Health Monitoring', () => {
recentCreationEvent: null,
recentConfirmationEvent: null,
isValidatorWhitelistDisabled: false,
isBaseStakeBelowThreshold: false
isBaseStakeBelowThreshold: false,
lastBlockIncludedInBatch: 800n,
}
}

Expand Down Expand Up @@ -141,13 +141,15 @@ describe('Assertion Health Monitoring', () => {
expect(alerts[0]).toBe(NO_CREATION_EVENTS_ALERT)
})

test('should alert when chain has activity but no recent creation events', async () => {
test('should alert when batches posted but no recent creation events', async () => {
const chainState = createBaseChainState()
// Set creation event to be older than the recent activity threshold (4 hours)
// Set creation event to be older than the recent activity threshold
chainState.childLatestCreatedBlock = {
...chainState.childLatestCreatedBlock!,
timestamp: NOW - BigInt(5 * 60 * 60), // 5 hours ago
number: 900n,
} as Block
chainState.lastBlockIncludedInBatch = 1000n

const alerts = await analyzeAssertionEvents(
chainState,
Expand All @@ -162,6 +164,24 @@ describe('Assertion Health Monitoring', () => {
expect(alerts).toContain(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
})

test('should NOT alert when no batches posted (low activity)', async () => {
const chainState = createBaseChainState()
chainState.childLatestCreatedBlock = {
...chainState.childLatestCreatedBlock!,
timestamp: NOW - BigInt(5 * 60 * 60), // 5 hours ago
number: 900n,
} as Block
chainState.lastBlockIncludedInBatch = 800n

const alerts = await analyzeAssertionEvents(
chainState,
mockChainInfo,
true
)

expect(alerts).not.toContain(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
})

test('should alert when no confirmation events exist', async () => {
const chainState = createBaseChainState()
chainState.childLatestConfirmedBlock = undefined
Expand Down Expand Up @@ -295,6 +315,8 @@ describe('Assertion Health Monitoring', () => {
number: 1800n,
} as Block

chainState.lastBlockIncludedInBatch = 1900n

// Set values to trigger confirmation delay
chainState.childCurrentBlock = {
...chainState.childCurrentBlock!,
Expand Down Expand Up @@ -531,13 +553,16 @@ describe('Assertion Health Monitoring', () => {
expect(alerts[0]).toBe(NO_CREATION_EVENTS_ALERT)
})

test('should alert when no recent creation events for non-BOLD chain', async () => {
test('should alert when batches posted but no recent creation events for non-BOLD chain', async () => {
const chainState = createBaseChainState()
// Set creation event to be older than the recent activity threshold (4 hours)
// Set creation event to be older than the recent activity threshold
chainState.childLatestCreatedBlock = {
...chainState.childLatestCreatedBlock!,
timestamp: NOW - BigInt(5 * 60 * 60), // 5 hours ago
number: 900n, // arbitrary block number for last assertion
} as Block
// any value > 900 triggers alert (batches exist beyond last assertion)
chainState.lastBlockIncludedInBatch = 1000n

const alerts = await analyzeAssertionEvents(
chainState,
Expand All @@ -548,8 +573,8 @@ describe('Assertion Health Monitoring', () => {
// Check if alerts array exists and has at least one element
expect(alerts.length).toBeGreaterThan(0)

// Check for expected alert
expect(alerts).toContain(NON_BOLD_NO_RECENT_CREATION_ALERT)
// Check for expected alert (same alert for both BOLD and non-BOLD)
expect(alerts).toContain(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
})

test('should alert when no confirmation events exist for non-BOLD chain', async () => {
Expand Down Expand Up @@ -610,19 +635,18 @@ describe('Assertion Health Monitoring', () => {
chainState.childLatestCreatedBlock = {
...chainState.childLatestCreatedBlock!,
timestamp: NOW - BigInt(7 * 24 * 60 * 60), // 7 days ago
number: 900n,
} as Block
chainState.lastBlockIncludedInBatch = 1000n

const alerts = await analyzeAssertionEvents(
chainState,
mockChainInfo,
false
)

// The implementation will generate other alerts, but not CREATION_EVENT_STUCK_ALERT
expect(alerts).not.toContain(CREATION_EVENT_STUCK_ALERT)

// But it should contain the NON_BOLD_NO_RECENT_CREATION_ALERT
expect(alerts).toContain(NON_BOLD_NO_RECENT_CREATION_ALERT)
expect(alerts).toContain(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
})

test('should generate alerts when extreme conditions are met for non-BOLD chain', async () => {
Expand All @@ -634,6 +658,8 @@ describe('Assertion Health Monitoring', () => {
number: 900n,
} as Block

chainState.lastBlockIncludedInBatch = 1000n

// Set parent chain blocks to indicate a delay
chainState.parentCurrentBlock = {
...chainState.parentCurrentBlock!,
Expand Down Expand Up @@ -664,7 +690,6 @@ describe('Assertion Health Monitoring', () => {

// Check for required alerts
expect(alerts).toContain(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
expect(alerts).toContain(NON_BOLD_NO_RECENT_CREATION_ALERT)
expect(alerts).toContain(CONFIRMATION_DELAY_ALERT)
})

Expand Down
4 changes: 1 addition & 3 deletions packages/assertion-monitor/alerts.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
export const NO_CREATION_EVENTS_ALERT = `No assertion creation events found`

export const CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT = `Chain activity detected, but no assertions created in the last 4 hours`
export const CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT = `Batches have been posted but no assertions created in the last 4 hours`

export const NO_CONFIRMATION_EVENTS_ALERT = `No assertion confirmation events found`

export const CONFIRMATION_DELAY_ALERT = `Confirmation period exceeded`

export const CREATION_EVENT_STUCK_ALERT = `Assertion event stuck in challenge period`

export const NON_BOLD_NO_RECENT_CREATION_ALERT = `No recent node creation events detected for non-BOLD chain`

export const VALIDATOR_WHITELIST_DISABLED_ALERT = `Validator whitelist disabled - this may indicate security concerns for Classic chains`

export const BOLD_LOW_BASE_STAKE_ALERT = `BoLD chain has low base stake (below 1 ETH) which may indicate restricted validation`
Expand Down
19 changes: 19 additions & 0 deletions packages/assertion-monitor/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
defineChain,
getContract,
http,
parseAbi,
type Block,
type Log,
} from 'viem'
Expand Down Expand Up @@ -386,6 +387,22 @@ export const fetchChainState = async ({
isBold
)

let lastBlockIncludedInBatch: bigint | undefined
try {
lastBlockIncludedInBatch = await parentClient.readContract({
address: childChainInfo.ethBridge.bridge as `0x${string}`,
abi: parseAbi([
'function sequencerReportedSubMessageCount() view returns (uint256)',
]),
functionName: 'sequencerReportedSubMessageCount',
})
} catch (error) {
console.error(
`Failed to query sequencerReportedSubMessageCount for ${childChainInfo.name}:`,
error
)
}

const chainState: ChainState = {
childCurrentBlock,
childLatestCreatedBlock,
Expand All @@ -399,6 +416,7 @@ export const fetchChainState = async ({
isBaseStakeBelowThreshold,
searchFromBlock: fromBlock,
searchToBlock: toBlock,
lastBlockIncludedInBatch,
}

console.log('Built chain state blocks:', {
Expand All @@ -408,6 +426,7 @@ export const fetchChainState = async ({
parentCurrentBlock: parentCurrentBlock.number,
parentBlockAtCreation: parentBlockAtCreation?.number,
parentBlockAtConfirmation: parentBlockAtConfirmation?.number,
lastBlockIncludedInBatch,
})

return chainState
Expand Down
44 changes: 15 additions & 29 deletions packages/assertion-monitor/monitoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
NO_CONFIRMATION_BLOCKS_WITH_CONFIRMATION_EVENTS_ALERT,
NO_CONFIRMATION_EVENTS_ALERT,
NO_CREATION_EVENTS_ALERT,
NON_BOLD_NO_RECENT_CREATION_ALERT,
VALIDATOR_WHITELIST_DISABLED_ALERT,
} from './alerts'
import {
Expand Down Expand Up @@ -103,12 +102,11 @@ export const analyzeAssertionEvents = async (
const {
doesLatestChildCreatedBlockExist,
doesLatestChildConfirmedBlockExist,
hasActivityWithoutRecentAssertions,
hasBatchesWithoutRecentAssertions,
noConfirmationsWithCreationEvents,
noConfirmedBlocksWithConfirmationEvents,
confirmationDelayExceedsPeriod,
creationEventStuckInChallengePeriod,
nonBoldMissingRecentCreation,
isValidatorWhitelistDisabledOnClassic,
isBaseStakeBelowThresholdOnBold,
} = generateConditionsForAlerts(chainInfo, chainState, isBold)
Expand All @@ -133,7 +131,7 @@ export const analyzeAssertionEvents = async (
alerts.push(NO_CONFIRMATION_BLOCKS_WITH_CONFIRMATION_EVENTS_ALERT)
}

if (hasActivityWithoutRecentAssertions) {
if (hasBatchesWithoutRecentAssertions) {
alerts.push(CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT)
}

Expand All @@ -149,10 +147,6 @@ export const analyzeAssertionEvents = async (
alerts.push(CREATION_EVENT_STUCK_ALERT)
}

if (nonBoldMissingRecentCreation) {
alerts.push(NON_BOLD_NO_RECENT_CREATION_ALERT)
}

return alerts
}

Expand All @@ -170,13 +164,12 @@ export const generateConditionsForAlerts = (
const currentTimeSeconds = Number(currentTimestamp / 1000n)

const {
childCurrentBlock,
childLatestCreatedBlock,
childLatestConfirmedBlock,
parentCurrentBlock,
parentBlockAtConfirmation,
recentCreationEvent,
recentConfirmationEvent,
lastBlockIncludedInBatch,
} = chainState

/**
Expand Down Expand Up @@ -206,20 +199,23 @@ export const generateConditionsForAlerts = (
const doesLatestChildConfirmedBlockExist = !!childLatestConfirmedBlock

/**
* Detects transaction processing in child chain not yet asserted in parent chain
* Normal in small amounts due to batching, concerning in large amounts
* Detects batches posted to parent chain but not yet asserted
* Only alerts when batches exist but no recent assertions cover them
*/
const hasActivityWithoutAssertions =
childCurrentBlock?.number &&
childLatestCreatedBlock?.number &&
childCurrentBlock.number > childLatestCreatedBlock.number
const childLatestCreatedBlockNumber = childLatestCreatedBlock?.number
const hasBatchesWithoutAssertions =
lastBlockIncludedInBatch !== undefined &&
childLatestCreatedBlockNumber !== undefined &&
childLatestCreatedBlockNumber !== null &&
lastBlockIncludedInBatch > childLatestCreatedBlockNumber

/**
* Critical for BOLD due to finality implications
* Indicates validator issues for both chain types
* Only alerts when batches have been posted but not asserted recently
*/
const hasActivityWithoutRecentAssertions =
hasActivityWithoutAssertions && !hasRecentCreationEvents
const hasBatchesWithoutRecentAssertions =
hasBatchesWithoutAssertions && !hasRecentCreationEvents

/**
* May indicate active challenges or technical issues with confirmation
Expand Down Expand Up @@ -272,15 +268,6 @@ export const generateConditionsForAlerts = (
childLatestCreatedBlock.timestamp <
BigInt(currentTimeSeconds - CHALLENGE_PERIOD_SECONDS)

/**
* Only alerts when activity exists without assertions
* May be normal for low-activity chains, hence contextual consideration required
*/
const nonBoldMissingRecentCreation =
!isBold &&
(!childLatestCreatedBlock ||
(!hasRecentCreationEvents && hasActivityWithoutAssertions))

/**
* Whether a Classic chain's validator whitelist is disabled, allowing
* unauthorized validators to post assertions.
Expand All @@ -301,12 +288,11 @@ export const generateConditionsForAlerts = (
doesLatestChildCreatedBlockExist,
doesLatestChildConfirmedBlockExist,
hasRecentCreationEvents,
hasActivityWithoutRecentAssertions,
hasBatchesWithoutRecentAssertions,
noConfirmationsWithCreationEvents,
noConfirmedBlocksWithConfirmationEvents,
confirmationDelayExceedsPeriod,
creationEventStuckInChallengePeriod,
nonBoldMissingRecentCreation,
isValidatorWhitelistDisabledOnClassic,
isBaseStakeBelowThresholdOnBold,
}
Expand Down
4 changes: 3 additions & 1 deletion packages/assertion-monitor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ export interface ChainState {
childLatestCreatedBlock?: Block
childLatestConfirmedBlock?: Block
parentCurrentBlock?: Block
parentBlockAtCreation?: Block
parentBlockAtCreation?: Block
parentBlockAtConfirmation?: Block
recentCreationEvent: CreationEvent | null
recentConfirmationEvent: ConfirmationEvent | null
isValidatorWhitelistDisabled: boolean
isBaseStakeBelowThreshold: boolean
searchFromBlock?: bigint
searchToBlock?: bigint
/** Last child block included in a batch */
lastBlockIncludedInBatch?: bigint
}
Loading