From f73a6d0015a02bcf1ecf751796574da474394199 Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Thu, 27 Feb 2025 10:21:17 -0500 Subject: [PATCH 1/2] fix: adds proper handling for permissionless bold chains --- packages/assertion-monitor/abi.ts | 7 ++++++ packages/assertion-monitor/alerts.ts | 4 +++- packages/assertion-monitor/blockchain.ts | 30 +++++++++++++++++++++++- packages/assertion-monitor/constants.ts | 3 +++ packages/assertion-monitor/index.ts | 13 +++++----- packages/assertion-monitor/monitoring.ts | 11 ++++++--- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/packages/assertion-monitor/abi.ts b/packages/assertion-monitor/abi.ts index 69a2e09..b8d56c9 100644 --- a/packages/assertion-monitor/abi.ts +++ b/packages/assertion-monitor/abi.ts @@ -18,6 +18,13 @@ export const boldABI = [ stateMutability: 'view', type: 'function', }, + { + inputs: [], + name: 'baseStake', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, ] as const export const ASSERTION_CREATED_EVENT = { diff --git a/packages/assertion-monitor/alerts.ts b/packages/assertion-monitor/alerts.ts index b1aa006..29cb4d2 100644 --- a/packages/assertion-monitor/alerts.ts +++ b/packages/assertion-monitor/alerts.ts @@ -10,6 +10,8 @@ export const CREATION_EVENT_STUCK_ALERT = `Assertion event stuck in challenge pe 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` +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` export const NO_CONFIRMATION_BLOCKS_WITH_CONFIRMATION_EVENTS_ALERT = `No assertion confirmation blocks found but confirmation events detected` diff --git a/packages/assertion-monitor/blockchain.ts b/packages/assertion-monitor/blockchain.ts index 3c290b9..109dd4a 100644 --- a/packages/assertion-monitor/blockchain.ts +++ b/packages/assertion-monitor/blockchain.ts @@ -17,7 +17,7 @@ import { boldABI, rollupABI, } from './abi' -import { CHUNK_SIZE } from './constants' +import { CHUNK_SIZE, MIN_BASE_STAKE_THRESHOLD } from './constants' import { AssertionDataError } from './errors' import { ChainState, ConfirmationEvent, CreationEvent } from './types' import { extractBoldBlockHash, extractClassicBlockHash } from './utils' @@ -39,6 +39,34 @@ export async function getValidatorWhitelistDisabled( return contract.read.validatorWhitelistDisabled() } +/** + * Checks if the baseStake for a BoLD chain is below a threshold that would indicate + * permissionless validation might be disabled or restricted. + * A very low baseStake could indicate that validation is not intended to be permissionless. + */ +export async function isBaseStakeBelowThreshold( + client: PublicClient, + rollupAddress: string, + thresholdInWei: bigint = MIN_BASE_STAKE_THRESHOLD +): Promise { + try { + const contract = getContract({ + address: rollupAddress as `0x${string}`, + abi: boldABI, + client, + }) + + const baseStake = await contract.read.baseStake() + console.log(`Base stake for rollup ${rollupAddress}: ${baseStake} wei`) + + return baseStake < thresholdInWei + } catch (error) { + console.error(`Error checking baseStake: ${error}`) + // Default to false if we can't check + return false + } +} + /** * Retrieves the latest block number that has been processed by the assertion chain. * Uses block hash from assertion data to track L2/L3 state progression. diff --git a/packages/assertion-monitor/constants.ts b/packages/assertion-monitor/constants.ts index 56b43a2..775e9f2 100644 --- a/packages/assertion-monitor/constants.ts +++ b/packages/assertion-monitor/constants.ts @@ -28,5 +28,8 @@ export const SEARCH_WINDOW_SECONDS = MAXIMUM_SEARCH_DAYS * SECONDS_IN_A_DAY /** Recent activity threshold in seconds (4 hours) */ export const RECENT_ACTIVITY_SECONDS = RECENT_CREATION_CHECK_HOURS * 60 * 60 +/** Minimum base stake threshold for BoLD chains (1 ETH in wei) */ +export const MIN_BASE_STAKE_THRESHOLD = BigInt('1000000000000000000') + /** Convert hours to seconds for timestamp comparison */ export const hoursToSeconds = (hours: number) => hours * 60 * 60 \ No newline at end of file diff --git a/packages/assertion-monitor/index.ts b/packages/assertion-monitor/index.ts index b279e4a..1dfc6f9 100644 --- a/packages/assertion-monitor/index.ts +++ b/packages/assertion-monitor/index.ts @@ -9,6 +9,7 @@ import { createChildChainClient, fetchChainState, getValidatorWhitelistDisabled, + isBaseStakeBelowThreshold, isBoldEnabled, } from './blockchain' import { getBlockTimeForChain, getChainFromId } from './chains' @@ -141,16 +142,16 @@ export const checkChainForAssertionIssues = async ( fromBlock, toBlock, }) - // Get validator whitelist status - const validatorWhitelistDisabled = await getValidatorWhitelistDisabled( - parentClient, - childChainInfo.ethBridge.rollup - ) + + // Check validation permissioning based on chain type + const validationPermissioningIssue = isBold + ? await isBaseStakeBelowThreshold(parentClient, childChainInfo.ethBridge.rollup) + : await getValidatorWhitelistDisabled(parentClient, childChainInfo.ethBridge.rollup); const alerts = await analyzeAssertionEvents( chainState, childChainInfo, - validatorWhitelistDisabled, + validationPermissioningIssue, isBold ) if (alerts.length > 0) { diff --git a/packages/assertion-monitor/monitoring.ts b/packages/assertion-monitor/monitoring.ts index b759a98..12f3180 100644 --- a/packages/assertion-monitor/monitoring.ts +++ b/packages/assertion-monitor/monitoring.ts @@ -1,5 +1,6 @@ import { ChildNetwork as ChainInfo } from '../utils' import { + BOLD_LOW_BASE_STAKE_ALERT, CHAIN_ACTIVITY_WITHOUT_ASSERTIONS_ALERT, CONFIRMATION_DELAY_ALERT, CREATION_EVENT_STUCK_ALERT, @@ -34,7 +35,7 @@ import { isEventRecent } from './utils' export const analyzeAssertionEvents = async ( chainState: ChainState, chainInfo: ChainInfo, - validatorWhitelistDisabled: boolean, + validationPermissioningIssue: boolean, isBold: boolean = true ): Promise => { const alerts: string[] = [] @@ -50,8 +51,12 @@ export const analyzeAssertionEvents = async ( nonBoldMissingRecentCreation, } = generateConditionsForAlerts(chainInfo, chainState, isBold) - if (validatorWhitelistDisabled) { - alerts.push(VALIDATOR_WHITELIST_DISABLED_ALERT) + if (validationPermissioningIssue) { + if (isBold) { + alerts.push(BOLD_LOW_BASE_STAKE_ALERT) + } else { + alerts.push(VALIDATOR_WHITELIST_DISABLED_ALERT) + } } if (!doesLatestChildCreatedBlockExist) { From 5679c37e94e500ca41fd96a11d5f137beff2350d Mon Sep 17 00:00:00 2001 From: Doug Lance <4741454+douglance@users.noreply.github.com> Date: Fri, 28 Feb 2025 08:26:34 -0500 Subject: [PATCH 2/2] improve logic around bold permissions --- packages/assertion-monitor/blockchain.ts | 14 +++++++++-- packages/assertion-monitor/index.ts | 10 +------- packages/assertion-monitor/monitoring.ts | 31 ++++++++++++++++++------ packages/assertion-monitor/types.ts | 2 ++ 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/assertion-monitor/blockchain.ts b/packages/assertion-monitor/blockchain.ts index 109dd4a..3a49a6c 100644 --- a/packages/assertion-monitor/blockchain.ts +++ b/packages/assertion-monitor/blockchain.ts @@ -44,7 +44,7 @@ export async function getValidatorWhitelistDisabled( * permissionless validation might be disabled or restricted. * A very low baseStake could indicate that validation is not intended to be permissionless. */ -export async function isBaseStakeBelowThreshold( +export async function fetchIsBaseStakeBelowThreshold( client: PublicClient, rollupAddress: string, thresholdInWei: bigint = MIN_BASE_STAKE_THRESHOLD @@ -58,7 +58,6 @@ export async function isBaseStakeBelowThreshold( const baseStake = await contract.read.baseStake() console.log(`Base stake for rollup ${rollupAddress}: ${baseStake} wei`) - return baseStake < thresholdInWei } catch (error) { console.error(`Error checking baseStake: ${error}`) @@ -373,6 +372,15 @@ export const fetchChainState = async ({ }) } + const isValidatorWhitelistDisabled = await getValidatorWhitelistDisabled( + parentClient, + childChainInfo.ethBridge.rollup + ) + const isBaseStakeBelowThreshold = await fetchIsBaseStakeBelowThreshold( + parentClient, + childChainInfo.ethBridge.rollup + ) + const chainState: ChainState = { childCurrentBlock, childLatestCreatedBlock, @@ -382,6 +390,8 @@ export const fetchChainState = async ({ parentBlockAtConfirmation, recentCreationEvent, recentConfirmationEvent, + isValidatorWhitelistDisabled, + isBaseStakeBelowThreshold, } console.log('Built chain state blocks:', { diff --git a/packages/assertion-monitor/index.ts b/packages/assertion-monitor/index.ts index 1dfc6f9..59cfe9e 100644 --- a/packages/assertion-monitor/index.ts +++ b/packages/assertion-monitor/index.ts @@ -8,9 +8,7 @@ import { import { createChildChainClient, fetchChainState, - getValidatorWhitelistDisabled, - isBaseStakeBelowThreshold, - isBoldEnabled, + isBoldEnabled } from './blockchain' import { getBlockTimeForChain, getChainFromId } from './chains' import { @@ -142,16 +140,10 @@ export const checkChainForAssertionIssues = async ( fromBlock, toBlock, }) - - // Check validation permissioning based on chain type - const validationPermissioningIssue = isBold - ? await isBaseStakeBelowThreshold(parentClient, childChainInfo.ethBridge.rollup) - : await getValidatorWhitelistDisabled(parentClient, childChainInfo.ethBridge.rollup); const alerts = await analyzeAssertionEvents( chainState, childChainInfo, - validationPermissioningIssue, isBold ) if (alerts.length > 0) { diff --git a/packages/assertion-monitor/monitoring.ts b/packages/assertion-monitor/monitoring.ts index 12f3180..4aac778 100644 --- a/packages/assertion-monitor/monitoring.ts +++ b/packages/assertion-monitor/monitoring.ts @@ -35,7 +35,6 @@ import { isEventRecent } from './utils' export const analyzeAssertionEvents = async ( chainState: ChainState, chainInfo: ChainInfo, - validationPermissioningIssue: boolean, isBold: boolean = true ): Promise => { const alerts: string[] = [] @@ -49,14 +48,16 @@ export const analyzeAssertionEvents = async ( confirmationDelayExceedsPeriod, creationEventStuckInChallengePeriod, nonBoldMissingRecentCreation, + isValidatorWhitelistDisabledOnClassic, + isBaseStakeBelowThresholdOnBold, } = generateConditionsForAlerts(chainInfo, chainState, isBold) - if (validationPermissioningIssue) { - if (isBold) { - alerts.push(BOLD_LOW_BASE_STAKE_ALERT) - } else { - alerts.push(VALIDATOR_WHITELIST_DISABLED_ALERT) - } + if (isValidatorWhitelistDisabledOnClassic) { + alerts.push(VALIDATOR_WHITELIST_DISABLED_ALERT) + } + + if (isBaseStakeBelowThresholdOnBold) { + alerts.push(BOLD_LOW_BASE_STAKE_ALERT) } if (!doesLatestChildCreatedBlockExist) { @@ -219,6 +220,20 @@ export const generateConditionsForAlerts = ( (!childLatestCreatedBlock || (!hasRecentCreationEvents && hasActivityWithoutAssertions)) + /** + * Whether a Classic chain's validator whitelist is disabled, allowing + * unauthorized validators to post assertions. + */ + const isValidatorWhitelistDisabledOnClassic = + !isBold && chainState.isValidatorWhitelistDisabled + + /** + * Whether a BoLD chain's base stake is below threshold, indicating restricted + * validator participation in dispute resolution. + */ + const isBaseStakeBelowThresholdOnBold = + isBold && chainState.isBaseStakeBelowThreshold + return { doesLatestChildCreatedBlockExist, doesLatestChildConfirmedBlockExist, @@ -229,5 +244,7 @@ export const generateConditionsForAlerts = ( confirmationDelayExceedsPeriod, creationEventStuckInChallengePeriod, nonBoldMissingRecentCreation, + isValidatorWhitelistDisabledOnClassic, + isBaseStakeBelowThresholdOnBold, } } diff --git a/packages/assertion-monitor/types.ts b/packages/assertion-monitor/types.ts index 0e77cfa..7d6903f 100644 --- a/packages/assertion-monitor/types.ts +++ b/packages/assertion-monitor/types.ts @@ -59,4 +59,6 @@ export interface ChainState { parentBlockAtConfirmation?: Block recentCreationEvent: CreationEvent | null recentConfirmationEvent: ConfirmationEvent | null + isValidatorWhitelistDisabled: boolean + isBaseStakeBelowThreshold: boolean }