From d360bbd18cea98d0a2d94d26001111a00dbd1fb1 Mon Sep 17 00:00:00 2001 From: lodekeeper Date: Sat, 4 Apr 2026 13:18:27 +0000 Subject: [PATCH 1/3] feat: add Gloas payload status to node notifier For Gloas (EIP-7732), the notifier now displays payload status (pending/empty/full) instead of the pre-Gloas execution status. When the head block has a FULL payload, the block number and hash are shown inline. Examples: Synced - slot: 100 - head: 0xabc... - payload: pending - ... Synced - slot: 100 - head: 0xabc... - payload: full(12345 0xdef...) - ... --- packages/beacon-node/src/node/notifier.ts | 29 +++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/beacon-node/src/node/notifier.ts b/packages/beacon-node/src/node/notifier.ts index 37a59a9c28d4..ef4d10dcb2ee 100644 --- a/packages/beacon-node/src/node/notifier.ts +++ b/packages/beacon-node/src/node/notifier.ts @@ -1,6 +1,6 @@ import {BeaconConfig} from "@lodestar/config"; -import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice"; -import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {ExecutionStatus, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice"; +import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params"; import { IBeaconStateView, computeEpochAtSlot, @@ -167,6 +167,13 @@ function getHeadExecutionInfo( return []; } + const fork = config.getForkName(headInfo.slot); + + // Gloas: show payload status (PENDING/EMPTY/FULL) instead of EL execution status + if (isForkPostGloas(fork)) { + return getGloasExecutionInfo(headInfo); + } + const executionStatusStr = headInfo.executionStatus.toLowerCase(); // Add execution status to notifier only if head is on/post bellatrix @@ -187,3 +194,21 @@ function getHeadExecutionInfo( return []; } + +/** Gloas-specific execution info showing payload status and block details */ +function getGloasExecutionInfo(headInfo: ProtoBlock): string[] { + const payloadStatusStr = + headInfo.payloadStatus === PayloadStatus.FULL + ? "full" + : headInfo.payloadStatus === PayloadStatus.EMPTY + ? "empty" + : "pending"; + + if (headInfo.payloadStatus === PayloadStatus.FULL && headInfo.executionPayloadBlockHash !== null) { + const hashInfo = prettyBytesShort(headInfo.executionPayloadBlockHash); + const numberInfo = headInfo.executionPayloadNumber; + return [`payload: ${payloadStatusStr}(${numberInfo} ${hashInfo})`]; + } + + return [`payload: ${payloadStatusStr}`]; +} From fdd80f44d0aabe0aba8a305a083af722c60eac7f Mon Sep 17 00:00:00 2001 From: lodekeeper Date: Sun, 19 Apr 2026 16:29:12 +0000 Subject: [PATCH 2/3] feat(notifier): shift log point to 5/6 of slot in gloas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Gloas, the head block's payload status is PENDING until PTC votes land (~2/3 of the slot) and the fork choice promotes the PENDING variant to FULL or EMPTY. Logging at half-slot (6s) surfaces "pending" even for healthy slots. Shift the notifier log point to 5/6 of the slot (10s on mainnet) for Gloas, so the head has transitioned to FULL/EMPTY by the time we print. Pre-Gloas keeps the half-slot cadence. 🤖 Generated with AI assistance --- packages/beacon-node/src/node/notifier.ts | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/beacon-node/src/node/notifier.ts b/packages/beacon-node/src/node/notifier.ts index ef4d10dcb2ee..0a80099063de 100644 --- a/packages/beacon-node/src/node/notifier.ts +++ b/packages/beacon-node/src/node/notifier.ts @@ -127,8 +127,9 @@ export async function runNodeNotifier(modules: NodeNotifierModules): Promise msPerHalfSlot ? msToNextSlot - msPerHalfSlot : msToNextSlot + msPerHalfSlot; + // at the 1st time we may miss the log point of the current clock slot + const msToSlotEndFromLogPoint = msPerSlot - msSlotOffset; + return msToNextSlot > msToSlotEndFromLogPoint + ? msToNextSlot - msToSlotEndFromLogPoint + : msToNextSlot + msSlotOffset; } - // after the 1st time always wait until middle of next clock slot - return msToNextSlot + msPerHalfSlot; + // after the 1st time always wait until the log point of the next clock slot + return msToNextSlot + msSlotOffset; } function getHeadExecutionInfo( From e399634f7ad47be10497f97915b11241e4a762cc Mon Sep 17 00:00:00 2001 From: lodekeeper Date: Sun, 19 Apr 2026 20:25:26 +0000 Subject: [PATCH 3/3] refactor(notifier): express log point as BPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align the notifier log point with the other slot-component timings (ATTESTATION_DUE_BPS, AGGREGATE_DUE_BPS, ...). Replace hardcoded msPerSlot/2 and (msPerSlot * 5) / 6 fractions with NOTIFIER_LOG_DUE_BPS (5000) and NOTIFIER_LOG_DUE_BPS_GLOAS (8333), and compute the offset via config.getSlotComponentDurationMs(). 🤖 Generated with AI assistance --- packages/beacon-node/src/node/notifier.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/beacon-node/src/node/notifier.ts b/packages/beacon-node/src/node/notifier.ts index 0a80099063de..9952541450d3 100644 --- a/packages/beacon-node/src/node/notifier.ts +++ b/packages/beacon-node/src/node/notifier.ts @@ -19,6 +19,11 @@ import {TimeSeries} from "../util/timeSeries.js"; /** Create a warning log whenever the peer count is at or below this value */ const WARN_PEER_COUNT = 1; +/** Notifier log point as basis points of the slot: 5000 BPS = half-slot (pre-Gloas). */ +const NOTIFIER_LOG_DUE_BPS = 5000; +/** Notifier log point for Gloas: 8333 BPS ≈ 5/6 of slot — past PTC deadline so the head has transitioned from PENDING to FULL/EMPTY. */ +const NOTIFIER_LOG_DUE_BPS_GLOAS = 8333; + type NodeNotifierModules = { network: INetwork; chain: IBeaconChain; @@ -153,7 +158,10 @@ function timeToNextLogPoint(config: BeaconConfig, chain: IBeaconChain, isFirstTi // In Gloas, PTC votes decide payload timeliness around 2/3 of the slot; log at 5/6 so the // head has transitioned from PENDING to FULL/EMPTY. Pre-Gloas keeps the half-slot cadence. const currentSlot = chain.clock.currentSlot; - const msSlotOffset = isForkPostGloas(config.getForkName(currentSlot)) ? (msPerSlot * 5) / 6 : msPerSlot / 2; + const logDueBps = isForkPostGloas(config.getForkName(currentSlot)) + ? NOTIFIER_LOG_DUE_BPS_GLOAS + : NOTIFIER_LOG_DUE_BPS; + const msSlotOffset = config.getSlotComponentDurationMs(logDueBps); if (isFirstTime) { // at the 1st time we may miss the log point of the current clock slot