From 95dc14cf71e107f54d9831e74ee74c3d69cd0510 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sat, 30 May 2026 16:21:11 +0100 Subject: [PATCH 1/4] fix: ignore PTC attestations for empty assigned slots --- .../src/chain/errors/payloadAttestation.ts | 2 ++ .../chain/validation/payloadAttestationMessage.ts | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/beacon-node/src/chain/errors/payloadAttestation.ts b/packages/beacon-node/src/chain/errors/payloadAttestation.ts index c9f15a2b657d..f6aa7f99dd52 100644 --- a/packages/beacon-node/src/chain/errors/payloadAttestation.ts +++ b/packages/beacon-node/src/chain/errors/payloadAttestation.ts @@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode { NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT", PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN", UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT", + INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT", INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK", INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER", INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE", @@ -18,6 +19,7 @@ export type PayloadAttestationErrorType = blockRoot: RootHex; } | {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex} + | {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot} | {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex} | {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex} | {code: PayloadAttestationErrorCode.INVALID_SIGNATURE}; diff --git a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts index 7c5d5ffe70fa..0ef7264856fe 100644 --- a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts +++ b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts @@ -61,13 +61,25 @@ 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.getBlockHexDefaultStatus(toRootHex(data.beaconBlockRoot)); + if (block === null) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT, blockRoot: toRootHex(data.beaconBlockRoot), }); } + // [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`, + // i.e. the block has `block.slot == data.slot`. + if (block.slot !== data.slot) { + throw new PayloadAttestationError(GossipAction.IGNORE, { + code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT, + blockRoot: toRootHex(data.beaconBlockRoot), + blockSlot: block.slot, + slot: data.slot, + }); + } + const state = chain.getHeadState(); if (!isStatePostGloas(state)) { throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`); From 9d730665eae6ed4f09a658a61e06a800952c1968 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sat, 30 May 2026 16:29:21 +0100 Subject: [PATCH 2/4] review --- .../src/chain/validation/payloadAttestationMessage.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts index 0ef7264856fe..5074abcb7e70 100644 --- a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts +++ b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts @@ -61,11 +61,12 @@ 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). - const block = chain.forkChoice.getBlockHexDefaultStatus(toRootHex(data.beaconBlockRoot)); - if (block === null) { + const blockRootHex = toRootHex(data.beaconBlockRoot); + const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex); + if (!block) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT, - blockRoot: toRootHex(data.beaconBlockRoot), + blockRoot: blockRootHex, }); } @@ -74,7 +75,7 @@ async function validatePayloadAttestationMessage( if (block.slot !== data.slot) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT, - blockRoot: toRootHex(data.beaconBlockRoot), + blockRoot: blockRootHex, blockSlot: block.slot, slot: data.slot, }); From d72763f6a310c55977c4315df1defd3fb5c04fca Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sat, 30 May 2026 16:32:28 +0100 Subject: [PATCH 3/4] refactor --- .../src/chain/validation/payloadAttestationMessage.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts index 5074abcb7e70..eff9a2fa9be6 100644 --- a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts +++ b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts @@ -61,12 +61,11 @@ 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). - const blockRootHex = toRootHex(data.beaconBlockRoot); - const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex); + const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot); if (!block) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT, - blockRoot: blockRootHex, + blockRoot: toRootHex(data.beaconBlockRoot), }); } @@ -75,7 +74,7 @@ async function validatePayloadAttestationMessage( if (block.slot !== data.slot) { throw new PayloadAttestationError(GossipAction.IGNORE, { code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT, - blockRoot: blockRootHex, + blockRoot: toRootHex(data.beaconBlockRoot), blockSlot: block.slot, slot: data.slot, }); From 41d9d321cd5fc5613ecb14695a807a21b93220c4 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sat, 30 May 2026 19:33:42 +0100 Subject: [PATCH 4/4] Apply suggestion from @nflaig --- .../src/chain/validation/payloadAttestationMessage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts index e3053f618cea..48e639017d7e 100644 --- a/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts +++ b/packages/beacon-node/src/chain/validation/payloadAttestationMessage.ts @@ -81,7 +81,6 @@ async function validatePayloadAttestationMessage( }); } - // [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