2
2
defaultdict ,
3
3
)
4
4
from collections .abc import (
5
+ AsyncIterable ,
5
6
Sequence ,
6
7
)
7
8
from typing import (
11
12
from multiaddr import (
12
13
Multiaddr ,
13
14
)
15
+ import trio
16
+ from trio import MemoryReceiveChannel , MemorySendChannel
14
17
15
18
from libp2p .abc import (
16
19
IPeerStore ,
@@ -40,6 +43,7 @@ class PeerStore(IPeerStore):
40
43
41
44
def __init__ (self ) -> None :
42
45
self .peer_data_map = defaultdict (PeerData )
46
+ self .addr_update_channels : dict [ID , MemorySendChannel [Multiaddr ]] = {}
43
47
44
48
def peer_info (self , peer_id : ID ) -> PeerInfo :
45
49
"""
@@ -53,6 +57,29 @@ def peer_info(self, peer_id: ID) -> PeerInfo:
53
57
return PeerInfo (peer_id , peer_data .get_addrs ())
54
58
raise PeerStoreError ("peer ID not found" )
55
59
60
+ def peer_ids (self ) -> list [ID ]:
61
+ """
62
+ :return: all of the peer IDs stored in peer store
63
+ """
64
+ return list (self .peer_data_map .keys ())
65
+
66
+ def clear_peerdata (self , peer_id : ID ) -> None :
67
+ """Clears the peer data of the peer"""
68
+
69
+ def valid_peer_ids (self ) -> list [ID ]:
70
+ """
71
+ :return: all of the valid peer IDs stored in peer store
72
+ """
73
+ valid_peer_ids : list [ID ] = []
74
+ for peer_id , peer_data in self .peer_data_map .items ():
75
+ if not peer_data .is_expired ():
76
+ valid_peer_ids .append (peer_id )
77
+ else :
78
+ peer_data .clear_addrs ()
79
+ return valid_peer_ids
80
+
81
+ # --------PROTO-BOOK--------
82
+
56
83
def get_protocols (self , peer_id : ID ) -> list [str ]:
57
84
"""
58
85
:param peer_id: peer ID to get protocols for
@@ -79,23 +106,31 @@ def set_protocols(self, peer_id: ID, protocols: Sequence[str]) -> None:
79
106
peer_data = self .peer_data_map [peer_id ]
80
107
peer_data .set_protocols (list (protocols ))
81
108
82
- def peer_ids (self ) -> list [ ID ] :
109
+ def remove_protocols (self , peer_id : ID , protocols : Sequence [ str ] ) -> None :
83
110
"""
84
- :return: all of the peer IDs stored in peer store
111
+ :param peer_id: peer ID to get info for
112
+ :param protocols: unsupported protocols to remove
85
113
"""
86
- return list (self .peer_data_map .keys ())
114
+ peer_data = self .peer_data_map [peer_id ]
115
+ peer_data .remove_protocols (protocols )
87
116
88
- def valid_peer_ids (self ) -> list [ID ]:
117
+ def supports_protocols (self , peer_id : ID , protocols : Sequence [ str ] ) -> list [str ]:
89
118
"""
90
- :return: all of the valid peer IDs stored in peer store
119
+ :return: all of the peer IDs stored in peer store
91
120
"""
92
- valid_peer_ids : list [ID ] = []
93
- for peer_id , peer_data in self .peer_data_map .items ():
94
- if not peer_data .is_expired ():
95
- valid_peer_ids .append (peer_id )
96
- else :
97
- peer_data .clear_addrs ()
98
- return valid_peer_ids
121
+ peer_data = self .peer_data_map [peer_id ]
122
+ return peer_data .supports_protocols (protocols )
123
+
124
+ def first_supported_protocol (self , peer_id : ID , protocols : Sequence [str ]) -> str :
125
+ peer_data = self .peer_data_map [peer_id ]
126
+ return peer_data .first_supported_protocol (protocols )
127
+
128
+ def clear_protocol_data (self , peer_id : ID ) -> None :
129
+ """Clears prtocoldata"""
130
+ peer_data = self .peer_data_map [peer_id ]
131
+ peer_data .clear_protocol_data ()
132
+
133
+ # ------METADATA---------
99
134
100
135
def get (self , peer_id : ID , key : str ) -> Any :
101
136
"""
@@ -121,6 +156,13 @@ def put(self, peer_id: ID, key: str, val: Any) -> None:
121
156
peer_data = self .peer_data_map [peer_id ]
122
157
peer_data .put_metadata (key , val )
123
158
159
+ def clear_metadata (self , peer_id : ID ) -> None :
160
+ """Clears metadata"""
161
+ peer_data = self .peer_data_map [peer_id ]
162
+ peer_data .clear_metadata ()
163
+
164
+ # -------ADDR-BOOK--------
165
+
124
166
def add_addr (self , peer_id : ID , addr : Multiaddr , ttl : int = 0 ) -> None :
125
167
"""
126
168
:param peer_id: peer ID to add address for
@@ -140,6 +182,13 @@ def add_addrs(self, peer_id: ID, addrs: Sequence[Multiaddr], ttl: int = 0) -> No
140
182
peer_data .set_ttl (ttl )
141
183
peer_data .update_last_identified ()
142
184
185
+ if peer_id in self .addr_update_channels :
186
+ for addr in addrs :
187
+ try :
188
+ self .addr_update_channels [peer_id ].send_nowait (addr )
189
+ except trio .WouldBlock :
190
+ pass # Or consider logging / dropping / replacing stream
191
+
143
192
def addrs (self , peer_id : ID ) -> list [Multiaddr ]:
144
193
"""
145
194
:param peer_id: peer ID to get addrs for
@@ -165,7 +214,7 @@ def clear_addrs(self, peer_id: ID) -> None:
165
214
166
215
def peers_with_addrs (self ) -> list [ID ]:
167
216
"""
168
- :return: all of the peer IDs which has addrs stored in peer store
217
+ :return: all of the peer IDs which has addrsfloat stored in peer store
169
218
"""
170
219
# Add all peers with addrs at least 1 to output
171
220
output : list [ID ] = []
@@ -179,6 +228,27 @@ def peers_with_addrs(self) -> list[ID]:
179
228
peer_data .clear_addrs ()
180
229
return output
181
230
231
+ async def addr_stream (self , peer_id : ID ) -> AsyncIterable [Multiaddr ]:
232
+ """
233
+ Returns an async stream of newly added addresses for the given peer.
234
+
235
+ This function allows consumers to subscribe to address updates for a peer
236
+ and receive each new address as it is added via `add_addr` or `add_addrs`.
237
+
238
+ :param peer_id: The ID of the peer to monitor address updates for.
239
+ :return: An async iterator yielding Multiaddr instances as they are added.
240
+ """
241
+ send : MemorySendChannel [Multiaddr ]
242
+ receive : MemoryReceiveChannel [Multiaddr ]
243
+
244
+ send , receive = trio .open_memory_channel (0 )
245
+ self .addr_update_channels [peer_id ] = send
246
+
247
+ async for addr in receive :
248
+ yield addr
249
+
250
+ # -------KEY-BOOK---------
251
+
182
252
def add_pubkey (self , peer_id : ID , pubkey : PublicKey ) -> None :
183
253
"""
184
254
:param peer_id: peer ID to add public key for
@@ -239,6 +309,45 @@ def add_key_pair(self, peer_id: ID, key_pair: KeyPair) -> None:
239
309
self .add_pubkey (peer_id , key_pair .public_key )
240
310
self .add_privkey (peer_id , key_pair .private_key )
241
311
312
+ def peer_with_keys (self ) -> list [ID ]:
313
+ """Returns the peer_ids for which keys are stored"""
314
+ return [
315
+ peer_id
316
+ for peer_id , pdata in self .peer_data_map .items ()
317
+ if pdata .pubkey is not None
318
+ ]
319
+
320
+ def clear_keydata (self , peer_id : ID ) -> None :
321
+ """Clears the keys of the peer"""
322
+ peer_data = self .peer_data_map [peer_id ]
323
+ peer_data .clear_keydata ()
324
+
325
+ # --------METRICS--------
326
+
327
+ def record_latency (self , peer_id : ID , RTT : float ) -> None :
328
+ """
329
+ Records a new latency measurement for the given peer
330
+ using Exponentially Weighted Moving Average (EWMA)
331
+
332
+ :param peer_id: peer ID to get private key for
333
+ :param RTT: the new latency value (round trip time)
334
+ """
335
+ peer_data = self .peer_data_map [peer_id ]
336
+ peer_data .record_latency (RTT )
337
+
338
+ def latency_EWMA (self , peer_id : ID ) -> float :
339
+ """
340
+ :param peer_id: peer ID to get private key for
341
+ :return: The latency EWMA value for that peer
342
+ """
343
+ peer_data = self .peer_data_map [peer_id ]
344
+ return peer_data .latency_EWMA ()
345
+
346
+ def clear_metrics (self , peer_id : ID ) -> None :
347
+ """Clear the latency metrics"""
348
+ peer_data = self .peer_data_map [peer_id ]
349
+ peer_data .clear_metrics ()
350
+
242
351
243
352
class PeerStoreError (KeyError ):
244
353
"""Raised when peer ID is not found in peer store."""
0 commit comments