test: deneb and electra gossip validation spec tests - #9372
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements and refines gossip validation for blob sidecars to align with the Deneb specification, including tracking seen sidecar tuples, validating proposer indices, and checking finalized ancestors. It also updates attestation slot range verification for EIP-7045 and adds pre-Capella checks for BLS-to-execution changes. Feedback suggests updating a comment to correctly describe the sidecar tracking tuple and reordering validation steps to perform the expensive KZG proof verification after the proposer check to improve efficiency.
| // [REJECT] The sidecar is proposed by the expected proposer_index | ||
| // (if shuffling is not available, IGNORE instead and MAY be queued for later) | ||
| const blockState = await chain.regen | ||
| .getBlockSlotState(parentBlock, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlock) | ||
| .catch(() => { | ||
| throw new BlobSidecarGossipError(GossipAction.IGNORE, { | ||
| code: BlobSidecarErrorCode.PARENT_UNKNOWN, | ||
| parentRoot, | ||
| blockRoot: blockHex, | ||
| slot: blobSlot, | ||
| }); | ||
| }); | ||
| if (blockState.getBeaconProposer(blobSlot) !== proposerIndex) { | ||
| throw new BlobSidecarGossipError(GossipAction.REJECT, { | ||
| code: BlobSidecarErrorCode.INCORRECT_PROPOSER, | ||
| proposerIndex, | ||
| }); | ||
| } |
There was a problem hiding this comment.
The proposer index validation (which may involve an expensive state regeneration via regen.getBlockSlotState) is currently performed after the expensive KZG proof verification (line 171).
Following the spec's recommended validation order, the proposer check should be performed before the blob (KZG) validation. If the proposer is incorrect, the sidecar should be rejected immediately without wasting CPU cycles on KZG verification.
There was a problem hiding this comment.
our check is as per spec, this is also dead code, generally we should follow spec order unless spec has a order that doesn't make sense in terms of running cheap vs. expensive checks first
There was a problem hiding this comment.
but this needs more review, ideally we wanna run cheap checks first
Performance Report✔️ no performance regression detected Full benchmark results
|
Adds runner support for the fulu `gossip_data_column_sidecar` networking spec tests from consensus-specs PR ChainSafe#5246. Mirrors the existing `gossip_blob_sidecar` arm: SSZ-decodes `fulu.DataColumnSidecar` and invokes `validateGossipFuluDataColumnSidecar` with the fixture's `subnet_id`. Builds on top of ChainSafe#9372. Does not address `gossip_partial_data_column_sidecar` (optional feature) nor the open `gossip_beacon_block__valid_at_blob_parameters_limit` zero-parent harness gap. 🤖 Generated with AI assistance
…p validation (#9624) ## Problem `validateExecutionPayloadBid` (Gloas execution payload bid gossip validation) does not implement the spec's `[REJECT] bid.builder_index < len(state.builders)` bounds check. It looks up the builder inside a `try/catch` meant to turn an out-of-range index into a `GossipReject`: ```ts let builder: gloas.Builder; try { builder = state.getBuilder(bid.builderIndex); } catch { throw new ExecutionPayloadBidError(GossipAction.REJECT, {code: BUILDER_NOT_ELIGIBLE, ...}); } if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) { ... } ``` But `state.getBuilder(i)` returns a **lazy** SSZ view (`builders.getReadonly(i)`) that is not bounds-checked eagerly. An out-of-range `builder_index` therefore does **not** throw at `getBuilder` — it throws `LeafNode has no right node` later, on deferred field access inside `isActiveBuilder` (`builder.depositEpoch`), which is outside the `try/catch`. The net effect is an **uncaught error** on the gossip validation path instead of a clean `REJECT`. ## Fix Add an explicit `bid.builder_index < len(state.builders)` bounds check up front using the existing `state.getBuildersLength()`, and drop the now-ineffective `try/catch`. ## Testing Found by running the consensus-specs [#5294](ethereum/consensus-specs#5294) Gloas networking reference tests (spec `v1.7.0-alpha.12`). The `gossip_execution_payload_bid__reject_builder_index_out_of_range` case now returns `REJECT` (previously an uncaught throw), with no regression across the rest of the `gossip_execution_payload_bid` suite (minimal + mainnet presets). > Note: these Gloas gossip validators currently have no unit-test coverage on `unstable`; the reftest suite (wired up in #9372) is their canonical coverage. 🤖 Generated with AI assistance Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
…ation (#9627) ## Problem The Gloas execution payload bid gossip validation checked the slot with an exact match and **no** `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance: ```ts const currentSlot = chain.clock.currentSlot; if (bid.slot !== currentSlot && bid.slot !== currentSlot + 1) { throw new ExecutionPayloadBidError(GossipAction.IGNORE, {code: INVALID_SLOT, ...}); } ``` Every other gossip slot check in Lodestar applies the disparity allowance (see `block.ts`, `blobSidecar.ts`, `attestation.ts`). Without it, a bid that arrives within `MAXIMUM_GOSSIP_CLOCK_DISPARITY` of a slot boundary is wrongly `IGNORE`d. ## Fix Implement the spec's `is_within_slot_range(state, bid.slot, 1, current_time_ms)` semantics: the current time must fall within `[start(bid.slot - 1), start(bid.slot + 1)]`, extended by `± MAXIMUM_GOSSIP_CLOCK_DISPARITY` on both ends — i.e. the clock is in slot `bid.slot - 1` (bid.slot is the *next* slot) or `bid.slot` (the *current* slot). Implemented with `chain.clock.msFromSlot(...)` because the disparity boundary is **sub-slot**: the passing/failing reftests are exactly **1 ms** apart at the edge (e.g. `95500` valid vs `95499` ignore; `108500` valid vs `108501` ignore). The slot-granular helpers (`currentSlotWithGossipDisparity` / `slotWithFutureTolerance`) floor to integer slots and produce identical values on either side of that 1 ms boundary, so they cannot express this check — a millisecond-precise comparison is required. ## Testing Found by running the consensus-specs [#5294](ethereum/consensus-specs#5294) Gloas networking reference tests (spec `v1.7.0-alpha.12`). All five `gossip_execution_payload_bid` slot cases now pass (minimal + mainnet): - `valid_slot_at_lower_disparity`, `valid_slot_at_upper_disparity` — now `valid` (were `ignore`) - `ignore_slot_outside_lower_disparity`, `ignore_slot_outside_upper_disparity`, `ignore_slot_too_far_future` — still `ignore` (no regression on the 1 ms-outside boundary) > Note: these Gloas gossip validators currently have no unit-test coverage on `unstable`; the reftest suite (wired up in #9372) is their canonical coverage. 🤖 Generated with AI assistance --------- Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
See ethereum/consensus-specs#5146 and ethereum/consensus-specs#5238