fix: refresh next-epoch proposer duties before the gloas fork#9590
fix: refresh next-epoch proposer duties before the gloas fork#9590nflaig wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the BlockDutiesService to re-poll next-epoch proposer duties on every slot during the final pre-Gloas epoch. This ensures that proposer preferences remain fresh and any dependent root shifts (e.g., due to a reorg) are picked up, since the v1 head event does not expose the next-epoch dependent root. A unit test has been added to verify this behavior. The reviewer pointed out that on the first slot of the final pre-Gloas epoch, runEveryEpochTask already pre-fetches the upcoming Gloas epoch duties, making the call in runEverySlotTask redundant on that slot. A suggestion was provided to skip the first slot of the epoch to avoid duplicate API requests.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| } else if ( | ||
| !isForkPostGloas(this.config.getForkName(slot)) && | ||
| isForkPostGloas(this.config.getForkName(nextEpochStartSlot)) | ||
| ) { |
There was a problem hiding this comment.
At the start of the final pre-Gloas epoch (the first slot of the epoch), runEveryEpochTask already pre-fetches the upcoming Gloas epoch duties via pollBeaconProposers(nextEpoch). Calling pollBeaconProposers(nextEpoch) again in runEverySlotTask on the very first slot results in a redundant concurrent API request to the beacon node.
We can avoid this redundant call by ensuring we do not re-poll on the first slot of the epoch.
| } else if ( | |
| !isForkPostGloas(this.config.getForkName(slot)) && | |
| isForkPostGloas(this.config.getForkName(nextEpochStartSlot)) | |
| ) { | |
| } else if ( | |
| slot !== computeStartSlotAtEpoch(computeEpochAtSlot(slot)) && | |
| !isForkPostGloas(this.config.getForkName(slot)) && | |
| isForkPostGloas(this.config.getForkName(nextEpochStartSlot)) | |
| ) { |
I had claude open this PR because of #9571 (comment) but this is not something we should worry about gonna close this again |
Performance Report✔️ no performance regression detected Full benchmark results
|
Motivation
Follow-up to #9571. In the final pre-Gloas epoch the validator submits proposer preferences for the first Gloas slots, which reads the next epoch's proposer duties from
BlockDutiesService. Pre-Gloas,onNewHeadcannot reorg-refresh those duties since the v1 head event does not expose the next-epoch proposer dep_root, and the boundary poll only fetches the next epoch when it is still pre-Gloas. A reorg in the lastSUBMIT_BEFORE_PROPOSAL_SLOTSthat shifts the Gloas epoch's dep_root therefore leaves the cached duties stale, the resubmit-on-shift check never fires, and the preferences go out under a stale dep_root which the beacon node ignores.Description
Re-poll the next epoch each slot during the final pre-Gloas epoch so a dep_root shift is picked up and the pre-fork preferences resubmit under the current root.