fix: get attestation block head#9500
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the fork-choice logic to resolve the specific payload-status variant (PENDING, EMPTY, or FULL) that an attestation votes for based on its slot and index, introducing the getAttestationHeadBlock helper. This helper is integrated into the gossip validation path for attestations and aggregates. The reviewer identified a critical issue in the error handling within verifyHeadBlockIsKnown: catching all errors and throwing a REJECT action is incorrect when the error is ATTESTS_TO_FUTURE_BLOCK, which should instead trigger an IGNORE action. A code suggestion was provided to properly differentiate these error cases.
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.
| let headBlock: ProtoBlock | null; | ||
| try { | ||
| headBlock = chain.forkChoice.getAttestationHeadBlock(toRootHex(beaconBlockRoot), attestationSlot, index); | ||
| } catch (_e) { | ||
| throw new AttestationError(GossipAction.REJECT, { | ||
| code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE, | ||
| attDataIndex: index, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Catching all errors from getAttestationHeadBlock and blindly throwing INVALID_PAYLOAD_STATUS_VALUE with GossipAction.REJECT is problematic. If the error thrown is ATTESTS_TO_FUTURE_BLOCK (e.g., when attestationSlot < headBlock.slot), the attestation is attesting to a future block. According to the gossip validation rules, such attestations should not be considered (i.e., GossipAction.IGNORE), rather than being rejected as having an invalid payload status value.
We should inspect the caught error and handle ATTESTS_TO_FUTURE_BLOCK separately (e.g., by throwing an AttestationError with GossipAction.IGNORE and an appropriate error code like UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT or similar).
| let headBlock: ProtoBlock | null; | |
| try { | |
| headBlock = chain.forkChoice.getAttestationHeadBlock(toRootHex(beaconBlockRoot), attestationSlot, index); | |
| } catch (_e) { | |
| throw new AttestationError(GossipAction.REJECT, { | |
| code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE, | |
| attDataIndex: index, | |
| }); | |
| } | |
| let headBlock: ProtoBlock | null; | |
| try { | |
| headBlock = chain.forkChoice.getAttestationHeadBlock(toRootHex(beaconBlockRoot), attestationSlot, index); | |
| } catch (e) { | |
| if (e && typeof e === "object" && "err" in e && (e as any).err?.code === "ATTESTS_TO_FUTURE_BLOCK") { | |
| throw new AttestationError(GossipAction.IGNORE, { | |
| code: AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT, | |
| root: toRootHex(beaconBlockRoot), | |
| }); | |
| } | |
| throw new AttestationError(GossipAction.REJECT, { | |
| code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE, | |
| attDataIndex: index, | |
| }); | |
| } |
Performance Report🚀🚀 Significant benchmark improvement detected
Full benchmark results
|
Motivation
verifyHeadBlockIsKnown()is always with default statusDescription
forkchoice. getAttestationHeadBlock()getAttestationPayloadStatus()AI Assistance Disclosure