Skip to content

Conversation

@aarshkshah1992
Copy link

@aarshkshah1992 aarshkshah1992 commented Nov 21, 2025

What type of PR is this?
Feature

What does this PR do? Why is it needed?

This PR overhauls the beacon node's Gossipsub management system to make it more testable, maintainable and readable. It also introduces proactive peer discovery and connection management for gossipsub peers that advertise subnets we're interested in.

This PR introduces four major components:

  1. Topic abstractions and a declarative fork based schedule
    Adds topic interfaces and implementations for all topics (without subnets, with static subnets and dynamic subnets) that the beacon-node should subscribe to/publish to. It also implements a declarative fork/epoch based schedule for subscribing/unsubscribing to these topics.

  2. A Gossipsub controller
    This controller is the heartbeat of the system and is responsible for ensuring that the beacon-node is subscribed to the right set of topics across forks and across slots & epochs within a fork. It manages subscriptions for topics without subnets, topics with static subnets and topics with dynamic subnets. It ensures we subscribe and unsubscribe from topics based on a pre-determined declarative fork based subscription schedule. It also ensures we unsubscribe from subnetted topics when we leave a subnet and subscribe to a

  3. A DiscV5 peer crawler for indexing gossipsub peers
    This crawler periodically does random walks on the discv5 network to index peers that advertise subnets we're a part of/we want to publish to. It also pings them to ensure they are reachable and discards unreachable peers.

  4. A peer controller
    This component proactively and periodically looks up the crawler for peers that subscribe to topics we want to subscribe to or publish to for a given slot and connects to them. It ensures that the mesh for each of this topics has enough high score peers in it.

This PR pushes details about identifying topics from subnets into the topic abstractions so that the rest of the system only has to work with topics which are strings. This simplifies a lot of code that deals with discovering and dialing to peers that are parts of specific subnets and for subscribing to topic based subnets.

It also removes a bunch of legacy code in subscriber.go and subnets.go that we no longer need after this overhaul.

Which issues(s) does this PR fix?

Fixes #

Other notes for review

Acknowledgements

@aarshkshah1992 aarshkshah1992 changed the title feat: a Gossipsub control pane with topic abstractions. a peer crawler and a peer controller [WIP: Do Not Review] feat: a Gossipsub control pane with topic abstractions. a peer crawler and a peer controller Nov 21, 2025
@aarshkshah1992 aarshkshah1992 self-assigned this Nov 21, 2025
@aarshkshah1992 aarshkshah1992 changed the title [WIP: Do Not Review] feat: a Gossipsub control pane with topic abstractions. a peer crawler and a peer controller feat: a Gossipsub control pane with topic abstractions. a peer crawler and a peer controller Nov 21, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors Gossipsub subscription management into a more modular and maintainable architecture by introducing a controller pattern with topic family abstractions, a peer crawler for discovering peers subscribed to specific topics, and a dialer for maintaining peer connections.

Key changes:

  • Introduces GossipsubController to manage topic subscriptions across fork boundaries with lifecycle management
  • Adds GossipsubPeerCrawler to discover and index peers by gossipsub topics using discv5
  • Implements GossipsubPeerDialer to maintain minimum peer counts per topic

Reviewed Changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
changelog/aarshkshah1992-gossipsub-peer-crawler.md Adds changelog entry for discV5 peer crawler
changelog/aarshkshah1992-gossipsub-control-pane.md Documents new Gossipsub controller with topic abstractions
beacon-chain/sync/topic_families_without_subnets.go Implements topic families for non-subnetted topics (blocks, exits, slashings, etc.)
beacon-chain/sync/topic_families_static_subnets.go Implements blob topic family with static subnet assignments
beacon-chain/sync/topic_families_dynamic_subnets.go Implements topic families with dynamic subnet subscriptions (attestations, sync committees, data columns)
beacon-chain/sync/gossipsub_topic_family.go Defines topic family interfaces and fork-aware scheduling logic
beacon-chain/sync/gossipsub_controller.go Implements main controller for managing topic family lifecycle across forks
beacon-chain/sync/gossipsub_base.go Provides base implementation for topic subscription and message handling
beacon-chain/sync/service.go Integrates gossipsub controller into sync service startup
beacon-chain/sync/fork_watcher.go Updates fork watcher to focus on RPC handlers only
beacon-chain/sync/subscriber.go Removes old subscription management code now handled by controller
beacon-chain/p2p/gossipsubcrawler/interface.go Defines interfaces for crawler and dialer components
beacon-chain/p2p/gossipsub_peer_crawler.go Implements peer discovery and indexing by gossipsub topics
beacon-chain/p2p/gossipsub_peer_controller.go Implements dialer to maintain minimum peers per topic
beacon-chain/p2p/service.go Initializes crawler and dialer in p2p service
beacon-chain/p2p/subnets.go Refactors subnet peer management to use new crawler/dialer
beacon-chain/p2p/broadcaster.go Updates broadcasting to use new topic helpers and dialer
Test files Adds comprehensive test coverage for new components

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aarshkshah1992 aarshkshah1992 force-pushed the feat/gossipsub-control-pane-peer-crawler-peer-controller branch from 09d886c to 8c3ecc4 Compare November 24, 2025 13:44
@aarshkshah1992 aarshkshah1992 force-pushed the feat/gossipsub-control-pane-peer-crawler-peer-controller branch from 8c3ecc4 to 09d886c Compare November 24, 2025 13:46
@aarshkshah1992 aarshkshah1992 changed the title feat: a Gossipsub control pane with topic abstractions. a peer crawler and a peer controller feat: a Gossipsub control pane with topic abstractions, a peer crawler and a peer controller Nov 24, 2025
…-crawler-peer-controller' into feat/gossipsub-control-pane-peer-crawler-peer-controller
@aarshkshah1992 aarshkshah1992 changed the title feat: a Gossipsub control pane with topic abstractions, a peer crawler and a peer controller feat: a Gossipsub control plane with topic abstractions, a peer crawler and a peer controller Nov 25, 2025
// Collect topics from dynamic families only, de-duplicated.
topicSet := make(map[string]struct{})
for _, df := range families {
topics, err := df.ExtractTopicsForNode(node)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have a struct that extracts part of an enode needed for topic abstraction ?

topics map[gossipsubcrawler.Topic]struct{}
}

type crawledPeers struct {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need feedback on packaging/layout as this is started in the sync package.

go func(node *enode.Node) {
defer g.pingSemaphore.Release(1)

if err := g.dv5.Ping(node); err != nil {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have multiple instances with different timeouts ?


func (g *GossipsubPeerDialer) dialLoop() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we bump up

// P2PMaxPeers defines a flag to specify the max number of peers in libp2p.
P2PMaxPeers = &cli.IntFlag{
	Name:  "p2p-max-peers",
	Usage: "The max number of p2p peers to maintain.",
	Value: 70,
}

var peersToDial []*enode.Node

for _, topic := range topics {
newPeers := g.peersForTopic(topic, peerPerTopic)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kasey has some good ideas on how to prioritise topics to dial to and how to rank peers based on their CGC scores. He will elaborate during review.

defer s.subnetLocker(subnet).Unlock()

if err := s.FindAndDialPeersWithSubnets(ctx, AttestationSubnetTopicFormat, forkDigest, minimumPeersPerSubnetForBroadcast, map[uint64]bool{subnet: true}); err != nil {
if err := s.gossipsubDialer.DialPeersForTopicBlocking(ctx, topic, minimumPeersPerSubnetForBroadcast); err != nil {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kasey has some good ideas on how to do async broadcast for data column topics which are starved of peers.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants