Skip to content

feat(beacon): score gossipsub peers with lighthouse's parameters - #656

Draft
MegaRedHand wants to merge 2 commits into
beacon-chain-integrationfrom
feat/beacon-gossipsub-scoring
Draft

MegaRedHand wants to merge 2 commits into
beacon-chain-integrationfrom
feat/beacon-gossipsub-scoring

Conversation

@MegaRedHand

Copy link
Copy Markdown
Collaborator

Motivation

The beacon wire had no gossipsub peer scoring (// TODO: set peer scoring params). Gossipsub kept exchanging with every peer, including ones that send invalid messages, break IWANT promises or cannot keep up, and the connection cap was the only bound on bad peers.

Slow peers are the visible case on the followers. The mainnet follower logged 96k gossipsub Send Queue full warnings in 22 h, from only 8 peers (one of them 60k). The Hoodi follower currently logs about 5000 a minute, almost all from 2 peers. Without scoring, nothing penalizes or drops those peers.

Changes

Area Change
p2p/src/beacon/scoring.rs (new) Lighthouse v8.2.2's gossipsub_scoring_parameters.rs, ported formula for formula: thresholds, peer score parameters, and per-topic parameters for beacon_block, beacon_aggregate_and_proof, all 64 beacon_attestation_{n} subnets, exits and slashings. MeshDeliveryGate (the P3 sync gate below). Unit tests for the derived parameters against lighthouse's values.
p2p/src/lib.rs Scoring is enabled on the beacon wire only (lean is unchanged). The p2p actor rebuilds the validator-count-dependent topic parameters every slot (RefreshPeerScoring, first run at startup) from the head's current-epoch shuffling and sends them to the swarm.
p2p/src/swarm_adapter.rs SwarmCommand::SetTopicScoreParams. On the existing 10 s tick, peers below the graylist are disconnected and peers are counted by score band.
storage/src/committee_cache.rs, types/beacon/committees.rs CommitteeCache::head_current_committees reads the pinned head's current-epoch shuffling (never builds one), and EpochCommittees::active_validator_count takes the count from its length, so the refresh needs no registry scan.
Metrics lean_gossipsub_peers_by_score{band}, lean_gossipsub_score_disconnects_total, lean_gossipsub_mesh_delivery_scoring.
Docs beacon_wire.md "Peer scoring" section; metrics.md.

Design decisions

  • Lighthouse's parameters. Teku, Lodestar and Grandine run the same ones, so a score here means what it means on most of mainnet. Thresholds: gossip -4000, publish -8000, graylist -16000. The slow-peer penalty (-10 per dropped message) is kept, since slow peers are what the followers see most.
  • mesh_n is ours (8), not lighthouse's 5. It only enters the first-message-delivery cap.
  • P3 (mesh deliveries) is gated on our own sync. Lighthouse subscribes to these topics only once synced. This node subscribes at startup and, while catching up, ignores every aggregate voting for a block it has not imported. A mesh peer credited with no aggregates scores about -19.5k on aggregates alone (below the graylist), so a restart would graylist our whole aggregate mesh for our own lag. P3 is off while the head lags the wall clock by more than 4 slots, and back on only after one epoch within that lag, so the counters have refilled. While off, its threshold and weight are zero but its cap is not, so the counters keep counting to their real ceiling.
  • The gate reads head lag directly, not SyncStatus. On the mainnet follower SyncStatus reported synced through a 10-minute catch-up up to 98 slots behind: its network-stall rule reads a lagging freshest-imported block as a stalled network.
  • Graylisted peers are disconnected; no ban list. Gossipsub alone only ignores a graylisted peer's RPCs, and the peer keeps a connection slot. A peer's negative score is retained for retain_score (100 epochs) after it leaves, so one that reconnects comes back graylisted and is dropped again on the next pass.
  • Columns, sync committee contributions and BLS changes stay unscored. This node Ignores every contribution and change (no consumer), and a delivery is only credited once accepted, so P3 there would penalize the mesh for a gap that is ours. Only Prysm scores columns.
  • All 64 attestation subnets get parameters, not just the backbone ones, because aggregator duties join others at runtime.

Not in this PR

  • Fork-digest changes at runtime: topics are named for the digest subscribed at startup. Once the digest can change, the old topics need their weight zeroed and the new ones need parameters (lighthouse's remove_topic_weight_except).
  • Fixing SyncStatus during beacon catch-up.

Testing

Run on the branch after merging beacon-chain-integration (125dce1).

Command Result
cargo clippy --workspace --all-targets --profile release-fast -- -D warnings clean
cargo fmt --all --check clean
cargo test --profile release-fast -p ethlambda-p2p --lib 218 passed, 1 ignored
cargo test --profile release-fast -p ethlambda-storage --lib 139 passed, 1 ignored

The beacon spec suites were not run: the only state-transition change makes committee_count_per_slot public, and fork choice is untouched.

The beacon wire had no way to push out peers that send invalid messages,
break IWANT promises or cannot keep up: gossipsub kept exchanging with all
of them, and the connection cap was the only bound on bad peers.

The parameters are lighthouse's, ported formula for formula. Teku, Lodestar
and Grandine run the same ones, so a score means here what it means on most
of mainnet. Two deviations:

- Mesh-delivery scoring (P3) is off while the head lags the wall clock by
  more than four slots, and back on only after an epoch within that.
  Lighthouse joins these topics only once synced. This node subscribes at
  startup and, while catching up, ignores every aggregate voting for a block
  it has not imported; a mesh peer credited with no aggregates scores below
  the graylist, so a restart would graylist the whole aggregate mesh for our
  own lag. SyncStatus cannot be the gate: its network-stall rule reported
  synced through a 98-slot catch-up on the mainnet follower.
- Peers below the graylist are disconnected every 10 s. Gossipsub alone only
  ignores them, and they keep a connection slot.

Columns, sync committee contributions and BLS changes stay unscored. This
node ignores every contribution and change for lack of a consumer, so P3
there would penalize the mesh for a gap that is ours.
@MegaRedHand MegaRedHand added the beacon Ethereum Beacon Chain client label Oct 2, 2026
MegaRedHand added a commit that referenced this pull request Oct 2, 2026
…-636-638-gloas-live

The scoring refresh names the dynamic topics for the digest current at
each slot, so on this branch's runtime fork schedule they follow a switch
within a slot; the docs say what does not follow (exit and slashing
parameters, old-digest weights).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beacon Ethereum Beacon Chain client

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant