-
Notifications
You must be signed in to change notification settings - Fork 210
Add full GossipSub v1.3 support (Extensions Control Message) #1231
Description
py-libp2p currently lacks full implementation of the GossipSub v1.3 specification (Extensions Control Message). The protocol ID /meshsub/1.3.0 is present and some extension plumbing exists, but the wire format, first-message semantics, and “at most once” rules from the spec are not satisfied. This issue tracks implementing v1.3 and the Topic Observation extension (from the ethresear.ch proposal) so that py-libp2p can interoperate with other implementations and support observing nodes that receive notifications without downloading full message copies.
Background
What v1.3 specifies
- Spec: gossipsub-v1.3.md — “GossipSub v1.3: Extensions Control Message” (Candidate Recommendation, r0 2025-06-23).
- Protocol ID:
/meshsub/1.3.0. - Core mechanism: Peers advertise supported extensions via an Extensions control message so that:
- New protocol behaviour can be added without a new protocol ID per extension.
- Extensions can be combined and versioned independently of GossipSub.
Key spec rules:
- First message: If a peer supports any extension, the Extensions control message MUST be included in the first message on the stream. It MUST NOT be sent more than once.
- Protobuf: Implementations MUST use the messages defined in the spec’s extensions.proto:
ControlMessagehasoptional ControlExtensions extensions = 6(a single message describing which extensions the sender supports).ControlExtensionsis a message with optional fields per extension (e.g.optional bool testExtension = 6492434).- Extension-specific RPC messages are optional top-level fields on
RPC(e.g.optional TestExtension testExtension = 6492434).
- Unknown extensions: Peers MUST ignore unknown extensions.
- No negotiation: Extensions describe the sending peer’s characteristics; each peer combines both sides’ advertised sets to define behaviour (per-extension spec).
Reference implementations
- go-libp2p: PR #630 – Gossipsub Extensions (merged).
- Router’s
AddPeer(peer, protocol, helloPacket *RPC) *RPCreceives the hello packet and returns the packet to send; extensions are added to that first message inextensions.AddPeer(). - Uses
ControlExtensions(single message) and trackssentExtensionsso the Extensions control message is only sent once per peer. - Treats a second Extensions control message from the same peer as misbehaviour (downscore).
- Router’s
Topic Observation extension (current implementation)
As part of this work we will implement the Topic Observation extension described in the GossipSub Topic Observation (proposed GossipSub 1.3) research post. This is not a future extension—it is in scope for the current implementation.
Motivation (from the research):
- Subscribing to a topic implies bandwidth amplification: you download or forward on the order of the mesh degree (e.g. ~D copies with mesh degree D). GossipSub 1.2's
IDONTWANTreduces copies but does not guarantee exactly how many. - Observing a topic means you do not receive full messages; you only get notifications when there is a new message (e.g. message IDs). If you need the payload, you can request it from the notifying peer once, so you download at most one copy. The actual message-request step can be defined separately; this extension deals with notifications.
Design (high level):
- Observing nodes tell subscribing peers (in the topic) that they want to observe the topic. Subscribing peers then send IHAVE to those observers when new messages arrive (IHAVE acts as a notification; observers are not expected to send IWANT in this flow).
- The relationship is unidirectional: observers receive notifications but do not send notifications or forward messages; they sit at the "border" of the network. Churn of observing nodes does not affect mesh stability.
- Protocol messages:
OBSERVE(request notifications for a topic) andUNOBSERVE(stop observing). AfterOBSERVE, the peer sendsIHAVEto the observer when there is a new message in the topic. In this use, IHAVE can be sent right after receiving a message, not only at heartbeats, and serves as a notification (no IWANT expected from the observer).
Implementing full v1.3 (Extensions Control Message) is required for Topic Observation, since it is an extension that uses the v1.3 extension mechanism. We will add both in this effort.
Current state in py-libp2p
- Protocol ID:
PROTOCOL_ID_V13 = TProtocol("/meshsub/1.3.0")is defined and accepted inadd_peer(). - Extension plumbing: There is an “extensions” feature check,
extension_handlers,send_extension(), andhandle_extension()that parse and emit a custom extension format.
Gaps vs the spec:
-
Protobuf format
- Spec:
ControlMessagehasoptional ControlExtensions extensions = 6(one message with optional fields per extension). Extension RPCs are optional fields onRPC. - Current:
libp2p/pubsub/pb/rpc.protousesrepeated ControlExtension extensions = 6withControlExtensionhavingname+data(opaque bytes). This does not match the spec’s extensions.proto and prevents wire-level interoperability with implementations that use the spec format.
- Spec:
-
First message
- Spec: Extensions control message must be in the first message on the stream.
- Current: The first message is built in
Pubsub.get_hello_packet()(subscriptions + senderRecord only). The router is not given the chance to add an Extensions control message to that packet, and the hello is sent without any control message. So the “first message” never carries Extensions.
-
At most once
- Spec: Extensions control message must not be sent more than once per peer.
- Current: There is no tracking of “already sent Extensions for this peer”; extensions can be sent later via
emit_control_message()(e.g. fromsend_extension()), so the “at most once” rule is not enforced.
-
Misbehaviour on duplicate
- Spec (implied): Sending Extensions more than once is invalid; implementations like go-libp2p downscore.
- Current: Duplicate Extensions from a peer are not detected or penalised.
Proposed scope for a PR
-
Adopt spec protobuf
- Align with the spec’s extensions.proto: introduce or mirror
ControlExtensionsand the RPC extension fields (e.g. TestExtension for interop). Either:- Integrate the spec’s extensions.proto (e.g. import or copy) and generate Python bindings, or
- Update
rpc.protoso thatControlMessagehasoptional ControlExtensions extensions = 6and RPC has the same optional extension fields as the spec, keeping compatibility with existing non-extension behaviour.
- Ensure “ignore unknown extensions” when reading
ControlExtensions.
- Align with the spec’s extensions.proto: introduce or mirror
-
First message
- Allow the router to contribute to the first message (e.g. extend the hello packet with the Extensions control message when the router supports v1.3 and has any extensions). Options:
- Change the API so that the router’s
add_peer(or a new method) receives the hello RPC and returns the RPC to send (similar to go-libp2p’sAddPeer(peer, protocol, hello) *RPC), or - Have
Pubsub.get_hello_packet()(or the code that builds the first message) call into the router to attach Extensions when applicable.
- Change the API so that the router’s
- Only include
ControlMessage.extensionswhen the selected protocol is v1.3+ and the node supports at least one extension.
- Allow the router to contribute to the first message (e.g. extend the hello packet with the Extensions control message when the router supports v1.3 and has any extensions). Options:
-
At most once
- Track per peer whether we have already sent the Extensions control message (e.g. a set or map of peer IDs). When building the first message, add Extensions only once; for all later messages to that peer, do not add Extensions again.
-
Duplicate Extensions from peer
- On handling an incoming RPC: if we have already recorded Extensions for this peer and the new RPC again contains
control.extensions, treat as protocol violation (e.g. downscore or ignore and log). Only parse and store the peer’s extensions from the first message where they appear.
- On handling an incoming RPC: if we have already recorded Extensions for this peer and the new RPC again contains
-
Topic Observation extension (current implementation)
- Implement the Topic Observation extension from the ethresear.ch proposal: register the extension in the v1.3 extensions mechanism, add
OBSERVEandUNOBSERVEcontrol messages (or the wire format agreed for this extension), and have subscribing peers sendIHAVEto observing peers when new messages arrive (as notifications). Ensure observers do not receive full message payloads unless they explicitly request them (if that path is in scope). - Add tests and docs for Topic Observation (observing vs subscribing, at-most-once notifications, UNOBSERVE cleanup).
- Implement the Topic Observation extension from the ethresear.ch proposal: register the extension in the v1.3 extensions mechanism, add