diff --git a/packages/beacon-node/src/chain/regen/interface.ts b/packages/beacon-node/src/chain/regen/interface.ts index 0c0032477e74..b1a89bbd91bd 100644 --- a/packages/beacon-node/src/chain/regen/interface.ts +++ b/packages/beacon-node/src/chain/regen/interface.ts @@ -22,6 +22,7 @@ export enum RegenCaller { validateGossipAttestation = "validateGossipAttestation", validateGossipVoluntaryExit = "validateGossipVoluntaryExit", validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid", + validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage", validateGossipProposerPreferences = "validateGossipProposerPreferences", onForkChoiceFinalized = "onForkChoiceFinalized", restApi = "restApi", diff --git a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts index 7c5d5ffe70fa..089fe743f820 100644 --- a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts +++ b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts @@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types"; import {toRootHex} from "@lodestar/utils"; import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js"; import {IBeaconChain} from "../index.js"; +import {RegenCaller} from "../regen/index.js"; export type PayloadAttestationValidationResult = { attDataRootHex: RootHex; @@ -61,22 +62,32 @@ async function validatePayloadAttestationMessage( // [IGNORE] The message's block `data.beacon_block_root` has been seen (via // gossip or non-gossip sources) (a client MAY queue attestation for processing // once the block is retrieved. Note a client might want to request payload after). - if (!chain.forkChoice.hasBlock(data.beaconBlockRoot)) { + const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot); + if (!block) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT, blockRoot: toRootHex(data.beaconBlockRoot), }); } - const state = chain.getHeadState(); - if (!isStatePostGloas(state)) { - throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`); - } - // [REJECT] The message's block `data.beacon_block_root` passes validation. // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice, // it is possible that the block didn't pass the validation + // Use the referenced block's branch state for the PTC committee check + const state = await chain.regen + .getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage) + .catch(() => { + throw new PayloadAttestationError(GossipAction.IGNORE, { + code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT, + blockRoot: toRootHex(data.beaconBlockRoot), + }); + }); + + if (!isStatePostGloas(state)) { + throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`); + } + // [REJECT] The message's validator index is within the payload committee in // `get_ptc(state, data.slot)`. The `state` is the head state corresponding to // processing the block up to the current slot as determined by the fork choice.