|
| 1 | +""" |
| 2 | +Network Event Types and Source Protocol. |
| 3 | +
|
| 4 | +This module defines the event types that flow from the network layer to the |
| 5 | +sync service, plus the abstract protocol that event sources must implement. |
| 6 | +
|
| 7 | +Event Flow |
| 8 | +---------- |
| 9 | +The network layer (libp2p or test mock) produces events as an async stream. |
| 10 | +The network service consumes these events and routes them to sync handlers. |
| 11 | +
|
| 12 | +:: |
| 13 | +
|
| 14 | + Event Source (async iterator) |
| 15 | + | |
| 16 | + Network Service (pattern matching dispatch) |
| 17 | + | |
| 18 | + +-- Gossip block events --> Sync block handler |
| 19 | + +-- Gossip attestation events --> Sync attestation handler |
| 20 | + +-- Peer status events --> Sync peer tracker |
| 21 | +""" |
| 22 | + |
| 23 | +from __future__ import annotations |
| 24 | + |
| 25 | +from dataclasses import dataclass |
| 26 | +from typing import Protocol, runtime_checkable |
| 27 | + |
| 28 | +from lean_spec.subspecs.containers import SignedBlockWithAttestation |
| 29 | +from lean_spec.subspecs.containers.attestation import SignedAttestation |
| 30 | +from lean_spec.subspecs.networking.gossipsub.topic import GossipTopic |
| 31 | +from lean_spec.subspecs.networking.reqresp.message import Status |
| 32 | +from lean_spec.subspecs.networking.types import PeerId |
| 33 | + |
| 34 | + |
| 35 | +@dataclass(frozen=True, slots=True) |
| 36 | +class GossipBlockEvent: |
| 37 | + """ |
| 38 | + Block received via gossip subscription. |
| 39 | +
|
| 40 | + Fired when a signed block arrives from the gossipsub network. |
| 41 | + The block may or may not have a known parent in the store. |
| 42 | + """ |
| 43 | + |
| 44 | + block: SignedBlockWithAttestation |
| 45 | + """The signed block with attestation proof.""" |
| 46 | + |
| 47 | + peer_id: PeerId |
| 48 | + """Peer that propagated this block to us.""" |
| 49 | + |
| 50 | + topic: GossipTopic |
| 51 | + """Topic the block was received on (includes fork digest).""" |
| 52 | + |
| 53 | + |
| 54 | +@dataclass(frozen=True, slots=True) |
| 55 | +class GossipAttestationEvent: |
| 56 | + """ |
| 57 | + Attestation received via gossip subscription. |
| 58 | +
|
| 59 | + Fired when a signed attestation arrives from the gossipsub network. |
| 60 | + """ |
| 61 | + |
| 62 | + attestation: SignedAttestation |
| 63 | + """The signed attestation.""" |
| 64 | + |
| 65 | + peer_id: PeerId |
| 66 | + """Peer that propagated this attestation to us.""" |
| 67 | + |
| 68 | + topic: GossipTopic |
| 69 | + """Topic the attestation was received on (includes fork digest).""" |
| 70 | + |
| 71 | + |
| 72 | +@dataclass(frozen=True, slots=True) |
| 73 | +class PeerStatusEvent: |
| 74 | + """ |
| 75 | + Peer sent their chain status. |
| 76 | +
|
| 77 | + Fired when a peer responds to or initiates a Status request. |
| 78 | + Contains the peer's view of the chain (head, finalized checkpoint). |
| 79 | + """ |
| 80 | + |
| 81 | + peer_id: PeerId |
| 82 | + """Peer that sent their status.""" |
| 83 | + |
| 84 | + status: Status |
| 85 | + """The peer's chain status (finalized checkpoint and head).""" |
| 86 | + |
| 87 | + |
| 88 | +@dataclass(frozen=True, slots=True) |
| 89 | +class PeerConnectedEvent: |
| 90 | + """ |
| 91 | + New peer connection established. |
| 92 | +
|
| 93 | + Fired when the transport layer establishes a new connection. |
| 94 | + The peer has not yet exchanged Status messages at this point. |
| 95 | + """ |
| 96 | + |
| 97 | + peer_id: PeerId |
| 98 | + """Peer that connected.""" |
| 99 | + |
| 100 | + |
| 101 | +@dataclass(frozen=True, slots=True) |
| 102 | +class PeerDisconnectedEvent: |
| 103 | + """ |
| 104 | + Peer disconnected. |
| 105 | +
|
| 106 | + Fired when a peer connection is closed: |
| 107 | + - either gracefully (Goodbye message) or |
| 108 | + - due to transport failure. |
| 109 | + """ |
| 110 | + |
| 111 | + peer_id: PeerId |
| 112 | + """Peer that disconnected.""" |
| 113 | + |
| 114 | + |
| 115 | +NetworkEvent = ( |
| 116 | + GossipBlockEvent |
| 117 | + | GossipAttestationEvent |
| 118 | + | PeerStatusEvent |
| 119 | + | PeerConnectedEvent |
| 120 | + | PeerDisconnectedEvent |
| 121 | +) |
| 122 | +"""Union of all network event types for pattern matching dispatch.""" |
| 123 | + |
| 124 | + |
| 125 | +@runtime_checkable |
| 126 | +class NetworkEventSource(Protocol): |
| 127 | + """ |
| 128 | + Abstract source of network events. |
| 129 | +
|
| 130 | + This protocol defines the interface that network implementations must |
| 131 | + provide. It is an async iterator that yields NetworkEvent objects. |
| 132 | +
|
| 133 | + Any class that implements async iteration over NetworkEvent can serve |
| 134 | + as a source. |
| 135 | +
|
| 136 | + Usage |
| 137 | + ----- |
| 138 | + :: |
| 139 | +
|
| 140 | + async for event in event_source: |
| 141 | + await handle_event(event) |
| 142 | +
|
| 143 | + The source controls backpressure. When the consumer is slow, the |
| 144 | + source naturally pauses due to async iteration semantics. |
| 145 | + """ |
| 146 | + |
| 147 | + def __aiter__(self) -> NetworkEventSource: |
| 148 | + """Return self as async iterator.""" |
| 149 | + ... |
| 150 | + |
| 151 | + async def __anext__(self) -> NetworkEvent: |
| 152 | + """ |
| 153 | + Yield the next network event. |
| 154 | +
|
| 155 | + Blocks until an event is available. |
| 156 | +
|
| 157 | + Returns: |
| 158 | + Next event from the network. |
| 159 | +
|
| 160 | + Raises: |
| 161 | + StopAsyncIteration: When no more events will arrive. |
| 162 | + """ |
| 163 | + ... |
0 commit comments