Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lean_spec/subspecs/networking/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
)
from .gossipsub.message import GossipsubMessage
from .gossipsub.parameters import GossipsubParameters
from .gossipsub.topic import GossipsubTopic
from .gossipsub.topic import GossipTopic
from .reqresp import (
BLOCKS_BY_ROOT_PROTOCOL_V1,
STATUS_PROTOCOL_V1,
Expand All @@ -22,7 +22,7 @@
"MESSAGE_DOMAIN_INVALID_SNAPPY",
"MESSAGE_DOMAIN_VALID_SNAPPY",
"GossipsubParameters",
"GossipsubTopic",
"GossipTopic",
"GossipsubMessage",
"BLOCKS_BY_ROOT_PROTOCOL_V1",
"STATUS_PROTOCOL_V1",
Expand Down
95 changes: 90 additions & 5 deletions src/lean_spec/subspecs/networking/gossipsub/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,97 @@
"""Gossipsub specs for the Lean Ethereum consensus specification."""
"""
Gossipsub Protocol Implementation
=================================

from .message import GossipsubMessage, MessageId
from .parameters import GossipsubParameters
from .topic import GossipsubTopic
Gossipsub is a mesh-based pubsub protocol combining:

1. **Eager push** within topic meshes for low-latency delivery
2. **Lazy pull** via gossip (IHAVE/IWANT) for reliability

Key Concepts
------------

- **Mesh**: Full message exchange with D peers per topic
- **Fanout**: Temporary peers for publish-only topics
- **Gossip**: IHAVE/IWANT for message dissemination to non-mesh peers
- **IDONTWANT**: Bandwidth optimization (v1.2)

References:
----------
- Gossipsub v1.0: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md
- Gossipsub v1.2: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md
- Ethereum P2P: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md
"""

from .control import (
ControlMessage,
Graft,
IDontWant,
IHave,
IWant,
Prune,
)
from .mcache import (
CacheEntry,
MessageCache,
SeenCache,
)
from .mesh import (
FanoutEntry,
MeshState,
TopicMesh,
)
from .message import GossipsubMessage, SnappyDecompressor
from .parameters import (
GossipsubParameters,
)
from .topic import (
ATTESTATION_TOPIC_NAME,
BLOCK_TOPIC_NAME,
ENCODING_POSTFIX,
TOPIC_PREFIX,
GossipTopic,
TopicKind,
format_topic_string,
parse_topic_string,
)
from .types import (
MessageId,
PeerId,
TopicId,
)

__all__ = [
# Message
"GossipsubMessage",
"SnappyDecompressor",
# Topic
"GossipTopic",
"TopicKind",
"TOPIC_PREFIX",
"ENCODING_POSTFIX",
"BLOCK_TOPIC_NAME",
"ATTESTATION_TOPIC_NAME",
"format_topic_string",
"parse_topic_string",
# Parameters
"GossipsubParameters",
"GossipsubTopic",
# Control
"ControlMessage",
"Graft",
"Prune",
"IHave",
"IWant",
"IDontWant",
# Mesh
"MeshState",
"TopicMesh",
"FanoutEntry",
# Cache
"MessageCache",
"SeenCache",
"CacheEntry",
# Types
"MessageId",
"PeerId",
"TopicId",
]
167 changes: 167 additions & 0 deletions src/lean_spec/subspecs/networking/gossipsub/control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""
Gossipsub Control Messages
==========================

Control messages orchestrate the gossip mesh topology and message propagation.

Overview
--------

Gossipsub uses control messages piggybacked on regular RPC messages to:

- Manage mesh membership (GRAFT/PRUNE)
- Enable lazy message propagation (IHAVE/IWANT)
- Reduce bandwidth for large messages (IDONTWANT)

Control Message Types
---------------------

+-------------+----------------------------------------------------------+
| Message | Purpose |
+=============+==========================================================+
| GRAFT | Request to join a peer's mesh for a topic |
+-------------+----------------------------------------------------------+
| PRUNE | Notify peer of removal from mesh |
+-------------+----------------------------------------------------------+
| IHAVE | Advertise message IDs available for a topic |
+-------------+----------------------------------------------------------+
| IWANT | Request full messages by their IDs |
+-------------+----------------------------------------------------------+
| IDONTWANT | Signal that specific messages are not needed (v1.2) |
+-------------+----------------------------------------------------------+

Protocol Flow
-------------

**Mesh Management:**

1. Peer A sends GRAFT to peer B for topic T
2. Peer B adds A to its mesh for T (or sends PRUNE if refusing)
3. Both peers now exchange full messages for topic T

**Lazy Pull:**

1. Peer A receives message M, adds to cache
2. Peer A sends IHAVE with M's ID to non-mesh peers
3. Peer B responds with IWANT if it needs M
4. Peer A sends full message M

References:
----------
- Gossipsub v1.0: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md
- Gossipsub v1.2: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md
"""

from __future__ import annotations

from lean_spec.subspecs.networking.gossipsub.types import MessageId
from lean_spec.types import StrictBaseModel


class Graft(StrictBaseModel):
"""Request to join a peer's mesh for a topic.

Sent when a peer wants to upgrade from gossip-only to full message exchange.

The receiving peer should add the sender to its mesh unless:

- The peer is already in the mesh
- The mesh is at capacity (|mesh| >= D_high)
- The peer is in a backoff period from a recent PRUNE
"""

topic_id: str
"""Topic identifier to join the mesh for."""


class Prune(StrictBaseModel):
"""Notification of removal from a peer's mesh.

Sent when:

- A peer unsubscribes from a topic
- Mesh size exceeds D_high during heartbeat
- A GRAFT is rejected

The pruned peer should not send GRAFT for this topic
until the backoff period expires.
"""

topic_id: str
"""Topic identifier being pruned from."""


class IHave(StrictBaseModel):
"""Advertisement of cached message IDs for a topic.

Sent to non-mesh peers during heartbeat to enable lazy pull.
Recipients can request any missing messages via IWANT.

Only includes messages from recent cache windows (mcache_gossip).
"""

topic_id: str
"""Topic the advertised messages belong to."""

message_ids: list[MessageId]
"""IDs of messages available in the sender's cache."""


class IWant(StrictBaseModel):
"""Request for full messages by their IDs.

Sent in response to IHAVE when the peer needs specific messages.
The peer should respond with the requested messages if still cached.
"""

message_ids: list[MessageId]
"""IDs of messages being requested."""


class IDontWant(StrictBaseModel):
"""Signal that specific messages are not needed.

Introduced in gossipsub v1.2 for bandwidth optimization.

Sent immediately after receiving a large message to tell mesh peers
not to forward their copy. Only used for messages exceeding the
IDONTWANT size threshold (typically 1KB).
"""

message_ids: list[MessageId]
"""IDs of messages the sender does not want to receive."""


class ControlMessage(StrictBaseModel):
"""Container for aggregated control messages.

Multiple control messages are batched into a single RPC
for efficiency. An RPC can contain any combination of
control message types.

Example::

control = ControlMessage(
grafts=[Graft(topic_id="blocks")],
ihaves=[IHave(topic_id="blocks", message_ids=[msg_id])],
)
"""

grafts: list[Graft] = []
"""GRAFT messages requesting mesh membership."""

prunes: list[Prune] = []
"""PRUNE messages notifying mesh removal."""

ihaves: list[IHave] = []
"""IHAVE messages advertising cached message IDs."""

iwants: list[IWant] = []
"""IWANT messages requesting full messages."""

idontwants: list[IDontWant] = []
"""IDONTWANT messages declining specific messages (v1.2)."""

def is_empty(self) -> bool:
"""Check if this control message contains no data."""
return not (self.grafts or self.prunes or self.ihaves or self.iwants or self.idontwants)
Loading
Loading