Skip to content

Verify data column sidecars before the duplicate check - #5560

Open
pucedoteth wants to merge 4 commits into
ethereum:masterfrom
pucedoteth:fix-sidecar-dedup-kzg-ordering
Open

Verify data column sidecars before the duplicate check#5560
pucedoteth wants to merge 4 commits into
ethereum:masterfrom
pucedoteth:fix-sidecar-dedup-kzg-ordering

Conversation

@pucedoteth

Copy link
Copy Markdown

Fixes #5451.

Problem

validate_blob_sidecar_gossip verifies the KZG proof before the duplicate check, but validate_data_column_sidecar_gossip does the opposite — in both Fulu and Gloas. For a sidecar whose tuple has already been seen and whose KZG proof is invalid:

path verdict
Deneb / Electra validate_blob_sidecar_gossip REJECTinvalid blob kzg proof
Fulu / Gloas validate_data_column_sidecar_gossip IGNOREalready seen sidecar ...

REJECT descores the peer, IGNORE does not, so the same malformed message is scored differently depending on which sidecar type carries it.

Why verify-then-dedup is the correct order

Fulu originally matched the blob path. The prose that #5246 converted into executable form read:

  • [REJECT] The sidecar's column data is valid as verified by verify_data_column_sidecar_kzg_proofs(sidecar).
  • [IGNORE] The sidecar is the first sidecar for the tuple (block_header.slot, block_header.proposer_index, sidecar.index) with valid header signature, sidecar inclusion proof, and kzg proof.

The duplicate check is defined over sidecars that already carry a valid signature, inclusion proof and KZG proof — so it cannot run first without changing its meaning. #5246 was a bulk prose-to-Python conversion ("Add executable gossip validation functions for fulu"); moving the check to the top looks incidental rather than intended.

Gloas's gossip functions were added later, in #5294, and inherited the reordered structure — so Gloas is not independent confirmation of a design choice. That is the one point where I differ from the issue, which states Gloas is already correct; it currently dedups first, like Fulu, so this PR changes both.

Change

Move the duplicate check after KZG verification in specs/fulu/p2p-interface.md and specs/gloas/p2p-interface.md. Fulu now matches validate_blob_sidecar_gossip step for step: inclusion proof → KZG → dedup → proposer → mark seen.

The diff is a pure move; seen.add(...) already happened at the end of the function in both, so the contents of the seen set are unchanged. Only the verdict for an invalid duplicate changes, from IGNORE to REJECT.

Alternative

If maintainers prefer dedup-first for data columns (cheap check first, skipping KZG work on duplicates), then the consistent fix is the opposite one — move dedup earlier in validate_blob_sidecar_gossip. I went with this direction because it restores the documented prose order and because rejecting invalid sidecars is itself the peer-scoring defense. Happy to flip it.

Test plan

Added test_gossip_data_column_sidecar__reject_already_seen_with_invalid_kzg_proofs, which delivers a valid sidecar and then the same tuple with corrupted KZG proofs. @with_fulu_and_later means the one test covers both forks.

  • Without the fix: AssertionError: assert 'ignore' == 'reject' — verified by reverting the two spec files and regenerating.
  • Reverting only specs/gloas/p2p-interface.md also fails it, confirming the test really exercises the Gloas path.
  • The existing __ignore_already_seen test is unaffected: a valid duplicate still returns IGNORE.
  • 245 passed across the fulu, gloas, deneb and electra networking suites; make lint clean.

🤖 Written with Claude Code. All results above come from a local make lint and pytest run against the generated pyspec.

`validate_blob_sidecar_gossip` verifies the KZG proof before the
duplicate check, but `validate_data_column_sidecar_gossip` does the
opposite in both Fulu and Gloas. For a sidecar whose tuple has already
been seen and whose KZG proof is invalid, the blob path returns REJECT
while the data column path returns IGNORE. REJECT descores the peer and
IGNORE does not, so the same malformed message is scored differently
depending on which sidecar type carries it.

Fulu originally matched the blob path. The prose it was converted from
in ethereum#5246 read:

    [REJECT] The sidecar's column data is valid as verified by
    verify_data_column_sidecar_kzg_proofs(sidecar).
    [IGNORE] The sidecar is the first sidecar for the tuple
    (block_header.slot, block_header.proposer_index, sidecar.index) with
    valid header signature, sidecar inclusion proof, and kzg proof.

The duplicate check is defined over sidecars that already carry a valid
signature, inclusion proof and KZG proof, so it cannot run first without
changing its meaning. Moving it ahead of those checks was incidental to
making the validations executable, not an intended change. Gloas's
gossip functions were added later in ethereum#5294 and inherited the order.

Move the duplicate check after KZG verification in both, restoring the
prose order and matching `validate_blob_sidecar_gossip`.

Fixes ethereum#5451

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added testing CI, actions, tests, testing infra fulu gloas labels Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fulu gloas testing CI, actions, tests, testing infra

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent dedup vs KZG ordering in sidecar gossip validation

1 participant