feat: add Gloas payload status to node notifier#18
Conversation
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...) - ...
|
@lodekeeper is this branch up to date with latest unstable on lodestar repo? |
|
@nflaig It wasn't — just merged latest |
|
Yes — branch is up to date with
|
| ? "full" | ||
| : headInfo.payloadStatus === PayloadStatus.EMPTY | ||
| ? "empty" | ||
| : "pending"; |
There was a problem hiding this comment.
I feel like this shouldn't be pending ever, we might need to re-think how out notifier works in gloas, my intuition right now is that instead of printing the log out half way through the slot we should rather do it at 10 seconds (or so) in the slot, and then either print out full or empty. what do you think @lodekeeper ?
There was a problem hiding this comment.
Agreed, good catch. Pushed fdd80f44d0: in Gloas we now log at 5/6 of the slot (10s on mainnet 12s slots, 5s on future 6s slots) instead of halfway. Pre-Gloas keeps the half-slot cadence to avoid shifting existing behavior.
Rationale for 5/6 rather than a fixed 10s: PTC votes land around 2/3 of the slot, so 5/6 gives ~2s margin for proto-array to update the head from PENDING → FULL/EMPTY. I kept the PENDING branch in getGloasExecutionInfo as a safety net (e.g. if PTC quorum never lands), but it should be rare at 10s.
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
nflaig
left a comment
There was a problem hiding this comment.
@lodekeeper can you show some example logs how this looks now, pre-gloas vs. post-gloas
|
Here's what the notifier output looks like. Format is consistent across forks — only the Pre-Gloas, synced (logs at T+6s on 12s slot): Post-Gloas, synced, payload FULL (logs at T+10s on 12s slot, after PTC vote cutoff at ~2/3 slot): Post-Gloas, synced, payload EMPTY (builder missed publish, PTC voted empty): Post-Gloas, PENDING (should be rare at 5/6 slot — only when PTC votes haven't arrived yet; before Syncing, post-Gloas: Happy to produce real captured logs from a gloas-devnet node if you want; the above is reconstructed from the code path ( |
|
@nflaig Here are representative log lines (15 char truncation for roots is Pre-Gloas (bellatrix+), logged at 6s into slot — unchanged: Post-Gloas, logged at 10s into slot (5/6 of 12s slot): FULL (happy path — payload arrived, PTC quorum voted present): EMPTY (payload didn't arrive, or PTC voted not present — head promotes to the EMPTY variant): PENDING (rare — kept as safety net; expected only if PTC quorum never lands, e.g. severe committee outage): Key shape change: pre-Gloas |
| // 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; |
There was a problem hiding this comment.
it would be better to define these a BPS values
There was a problem hiding this comment.
Done in e399634f7a. Introduced NOTIFIER_LOG_DUE_BPS (5000 = half-slot, pre-Gloas) and NOTIFIER_LOG_DUE_BPS_GLOAS (8333 ≈ 5/6 slot = 10s on 12s slots), and switched the offset computation to config.getSlotComponentDurationMs(logDueBps) to match the existing ATTESTATION_DUE_BPS / AGGREGATE_DUE_BPS pattern. Kept the constants module-local since this is purely notifier UX, not a spec timing — happy to lift them into chainConfig if you'd prefer.
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
|
@lodekeeper can you open a second PR similar to that to your lodekeeper/lodestar repo which always prints out the execution payload status of previous slot (based on what the next block build on by looking at the parent hash) |
|
@nflaig opened as #19 — adds a
Independent of #18 — branched off latest |
|
Parking advisor feedback here for later — not blockers, but things to address if this PR is what we decide to land (vs. the minimal #19 shape):
Captured from a 3-advisor review (Defender persona, Devil's Advocate, GPT-5.4 xhigh). 🤖 AI-assisted. |
|
@lodekeeper please close this and consider it done |
|
Closing per @nflaig request — superseded by direction in #18 (comment). |
Summary
Updates the node notifier to display Gloas-specific payload status information.
Before (pre-Gloas)
After (Gloas)
What changed
For Gloas blocks, the notifier now shows:
payload: pending— beacon block received, waiting for execution payloadpayload: empty— no payload was delivered for this blockpayload: full(number hash)— execution payload received and importedThis replaces the pre-Gloas
exec-block: valid/optimistic/syncing(...)display, which doesn't apply to Gloas's two-phase block lifecycle.Changes
packages/beacon-node/src/node/notifier.ts— NewgetGloasExecutionInfo()function, Gloas fork guard ingetHeadExecutionInfo()