koi-net 1.0.0b11__py3-none-any.whl → 1.0.0b12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/core.py +125 -125
- koi_net/identity.py +69 -69
- koi_net/network/graph.py +127 -127
- koi_net/network/interface.py +273 -275
- koi_net/network/request_handler.py +148 -148
- koi_net/network/response_handler.py +58 -58
- koi_net/processor/default_handlers.py +162 -162
- koi_net/processor/handler.py +59 -59
- koi_net/processor/interface.py +298 -298
- koi_net/processor/knowledge_object.py +122 -122
- koi_net/protocol/api_models.py +46 -46
- koi_net/protocol/consts.py +6 -6
- koi_net/protocol/edge.py +20 -20
- koi_net/protocol/event.py +50 -50
- koi_net/protocol/helpers.py +24 -24
- koi_net/protocol/node.py +16 -16
- {koi_net-1.0.0b11.dist-info → koi_net-1.0.0b12.dist-info}/METADATA +1 -1
- koi_net-1.0.0b12.dist-info/RECORD +24 -0
- {koi_net-1.0.0b11.dist-info → koi_net-1.0.0b12.dist-info}/licenses/LICENSE +21 -21
- koi_net-1.0.0b11.dist-info/RECORD +0 -24
- {koi_net-1.0.0b11.dist-info → koi_net-1.0.0b12.dist-info}/WHEEL +0 -0
koi_net/network/interface.py
CHANGED
|
@@ -1,276 +1,274 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from queue import Queue
|
|
3
|
-
import httpx
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
from rid_lib import RID
|
|
6
|
-
from rid_lib.core import RIDType
|
|
7
|
-
from rid_lib.ext import Cache
|
|
8
|
-
from rid_lib.types import KoiNetNode
|
|
9
|
-
from .graph import NetworkGraph
|
|
10
|
-
from .request_handler import RequestHandler
|
|
11
|
-
from .response_handler import ResponseHandler
|
|
12
|
-
from ..protocol.node import NodeType
|
|
13
|
-
from ..protocol.edge import EdgeType
|
|
14
|
-
from ..protocol.event import Event
|
|
15
|
-
from ..identity import NodeIdentity
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class EventQueueModel(BaseModel):
|
|
21
|
-
webhook: dict[KoiNetNode, list[Event]]
|
|
22
|
-
poll: dict[KoiNetNode, list[Event]]
|
|
23
|
-
|
|
24
|
-
type EventQueue = dict[RID, Queue[Event]]
|
|
25
|
-
|
|
26
|
-
class NetworkInterface:
|
|
27
|
-
"""A collection of functions and classes to interact with the KOI network."""
|
|
28
|
-
|
|
29
|
-
identity: NodeIdentity
|
|
30
|
-
cache: Cache
|
|
31
|
-
first_contact: str | None
|
|
32
|
-
graph: NetworkGraph
|
|
33
|
-
request_handler: RequestHandler
|
|
34
|
-
response_handler: ResponseHandler
|
|
35
|
-
poll_event_queue: EventQueue
|
|
36
|
-
webhook_event_queue: EventQueue
|
|
37
|
-
event_queues_file_path: str
|
|
38
|
-
|
|
39
|
-
def __init__(
|
|
40
|
-
self,
|
|
41
|
-
file_path: str,
|
|
42
|
-
first_contact: str | None,
|
|
43
|
-
cache: Cache,
|
|
44
|
-
identity: NodeIdentity
|
|
45
|
-
):
|
|
46
|
-
self.identity = identity
|
|
47
|
-
self.cache = cache
|
|
48
|
-
self.first_contact = first_contact
|
|
49
|
-
self.graph = NetworkGraph(cache, identity)
|
|
50
|
-
self.request_handler = RequestHandler(cache, self.graph)
|
|
51
|
-
self.response_handler = ResponseHandler(cache)
|
|
52
|
-
self.event_queues_file_path = file_path
|
|
53
|
-
|
|
54
|
-
self.poll_event_queue = dict()
|
|
55
|
-
self.webhook_event_queue = dict()
|
|
56
|
-
self._load_event_queues()
|
|
57
|
-
|
|
58
|
-
def _load_event_queues(self):
|
|
59
|
-
"""Loads event queues from storage."""
|
|
60
|
-
try:
|
|
61
|
-
with open(self.event_queues_file_path, "r") as f:
|
|
62
|
-
queues = EventQueueModel.model_validate_json(f.read())
|
|
63
|
-
|
|
64
|
-
for node in queues.poll.keys():
|
|
65
|
-
for event in queues.poll[node]:
|
|
66
|
-
queue = self.poll_event_queue.setdefault(node, Queue())
|
|
67
|
-
queue.put(event)
|
|
68
|
-
|
|
69
|
-
for node in queues.webhook.keys():
|
|
70
|
-
for event in queues.webhook[node]:
|
|
71
|
-
queue = self.webhook_event_queue.setdefault(node, Queue())
|
|
72
|
-
queue.put(event)
|
|
73
|
-
|
|
74
|
-
except FileNotFoundError:
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
def _save_event_queues(self):
|
|
78
|
-
"""Writes event queues to storage."""
|
|
79
|
-
events_model = EventQueueModel(
|
|
80
|
-
poll={
|
|
81
|
-
node: list(queue.queue)
|
|
82
|
-
for node, queue in self.poll_event_queue.items()
|
|
83
|
-
if not queue.empty()
|
|
84
|
-
},
|
|
85
|
-
webhook={
|
|
86
|
-
node: list(queue.queue)
|
|
87
|
-
for node, queue in self.webhook_event_queue.items()
|
|
88
|
-
if not queue.empty()
|
|
89
|
-
}
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
if len(events_model.poll) == 0 and len(events_model.webhook) == 0:
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
with open(self.event_queues_file_path, "w") as f:
|
|
96
|
-
f.write(events_model.model_dump_json(indent=2))
|
|
97
|
-
|
|
98
|
-
def push_event_to(self, event: Event, node: KoiNetNode, flush=False):
|
|
99
|
-
"""Pushes event to queue of specified node.
|
|
100
|
-
|
|
101
|
-
Event will be sent to webhook or poll queue depending on the node type and edge type of the specified node. If `flush` is set to `True`, the webhook queued will be flushed after pushing the event.
|
|
102
|
-
"""
|
|
103
|
-
logger.debug(f"Pushing event {event.event_type} {event.rid} to {node}")
|
|
104
|
-
|
|
105
|
-
node_profile = self.graph.get_node_profile(node)
|
|
106
|
-
if not node_profile:
|
|
107
|
-
logger.warning(f"Node {node!r} unknown to me")
|
|
108
|
-
|
|
109
|
-
# if there's an edge from me to the target node, override broadcast type
|
|
110
|
-
edge_profile = self.graph.get_edge_profile(
|
|
111
|
-
source=self.identity.rid,
|
|
112
|
-
target=node
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
if edge_profile:
|
|
116
|
-
if edge_profile.edge_type == EdgeType.WEBHOOK:
|
|
117
|
-
event_queue = self.webhook_event_queue
|
|
118
|
-
elif edge_profile.edge_type == EdgeType.POLL:
|
|
119
|
-
event_queue = self.poll_event_queue
|
|
120
|
-
else:
|
|
121
|
-
if node_profile.node_type == NodeType.FULL:
|
|
122
|
-
event_queue = self.webhook_event_queue
|
|
123
|
-
elif node_profile.node_type == NodeType.PARTIAL:
|
|
124
|
-
event_queue = self.poll_event_queue
|
|
125
|
-
|
|
126
|
-
queue = event_queue.setdefault(node, Queue())
|
|
127
|
-
queue.put(event)
|
|
128
|
-
|
|
129
|
-
if flush and event_queue is self.webhook_event_queue:
|
|
130
|
-
self.flush_webhook_queue(node)
|
|
131
|
-
|
|
132
|
-
def _flush_queue(self, event_queue: EventQueue, node: KoiNetNode) -> list[Event]:
|
|
133
|
-
"""Flushes a node's queue, returning list of events."""
|
|
134
|
-
queue = event_queue.get(node)
|
|
135
|
-
events = list()
|
|
136
|
-
if queue:
|
|
137
|
-
while not queue.empty():
|
|
138
|
-
event = queue.get()
|
|
139
|
-
logger.debug(f"Dequeued {event.event_type} '{event.rid}'")
|
|
140
|
-
events.append(event)
|
|
141
|
-
|
|
142
|
-
return events
|
|
143
|
-
|
|
144
|
-
def flush_poll_queue(self, node: KoiNetNode) -> list[Event]:
|
|
145
|
-
"""Flushes a node's poll queue, returning list of events."""
|
|
146
|
-
logger.debug(f"Flushing poll queue for {node}")
|
|
147
|
-
return self._flush_queue(self.poll_event_queue, node)
|
|
148
|
-
|
|
149
|
-
def flush_webhook_queue(self, node: KoiNetNode):
|
|
150
|
-
"""Flushes a node's webhook queue, and broadcasts events.
|
|
151
|
-
|
|
152
|
-
If node profile is unknown, or node type is not `FULL`, this operation will fail silently. If the remote node cannot be reached, all events will be requeued.
|
|
153
|
-
"""
|
|
154
|
-
|
|
155
|
-
logger.debug(f"Flushing webhook queue for {node}")
|
|
156
|
-
|
|
157
|
-
node_profile = self.graph.get_node_profile(node)
|
|
158
|
-
|
|
159
|
-
if not node_profile:
|
|
160
|
-
logger.warning(f"{node!r} not found")
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
if node_profile.node_type != NodeType.FULL:
|
|
164
|
-
logger.warning(f"{node!r} is a partial node!")
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
events = self._flush_queue(self.webhook_event_queue, node)
|
|
168
|
-
if not events: return
|
|
169
|
-
|
|
170
|
-
logger.debug(f"Broadcasting {len(events)} events")
|
|
171
|
-
|
|
172
|
-
try:
|
|
173
|
-
self.request_handler.broadcast_events(node, events=events)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
node
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
"
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
neighbors
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
node
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return events
|
|
275
|
-
|
|
1
|
+
import logging
|
|
2
|
+
from queue import Queue
|
|
3
|
+
import httpx
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from rid_lib import RID
|
|
6
|
+
from rid_lib.core import RIDType
|
|
7
|
+
from rid_lib.ext import Cache
|
|
8
|
+
from rid_lib.types import KoiNetNode
|
|
9
|
+
from .graph import NetworkGraph
|
|
10
|
+
from .request_handler import RequestHandler
|
|
11
|
+
from .response_handler import ResponseHandler
|
|
12
|
+
from ..protocol.node import NodeType
|
|
13
|
+
from ..protocol.edge import EdgeType
|
|
14
|
+
from ..protocol.event import Event
|
|
15
|
+
from ..identity import NodeIdentity
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EventQueueModel(BaseModel):
|
|
21
|
+
webhook: dict[KoiNetNode, list[Event]]
|
|
22
|
+
poll: dict[KoiNetNode, list[Event]]
|
|
23
|
+
|
|
24
|
+
type EventQueue = dict[RID, Queue[Event]]
|
|
25
|
+
|
|
26
|
+
class NetworkInterface:
|
|
27
|
+
"""A collection of functions and classes to interact with the KOI network."""
|
|
28
|
+
|
|
29
|
+
identity: NodeIdentity
|
|
30
|
+
cache: Cache
|
|
31
|
+
first_contact: str | None
|
|
32
|
+
graph: NetworkGraph
|
|
33
|
+
request_handler: RequestHandler
|
|
34
|
+
response_handler: ResponseHandler
|
|
35
|
+
poll_event_queue: EventQueue
|
|
36
|
+
webhook_event_queue: EventQueue
|
|
37
|
+
event_queues_file_path: str
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
file_path: str,
|
|
42
|
+
first_contact: str | None,
|
|
43
|
+
cache: Cache,
|
|
44
|
+
identity: NodeIdentity
|
|
45
|
+
):
|
|
46
|
+
self.identity = identity
|
|
47
|
+
self.cache = cache
|
|
48
|
+
self.first_contact = first_contact
|
|
49
|
+
self.graph = NetworkGraph(cache, identity)
|
|
50
|
+
self.request_handler = RequestHandler(cache, self.graph)
|
|
51
|
+
self.response_handler = ResponseHandler(cache)
|
|
52
|
+
self.event_queues_file_path = file_path
|
|
53
|
+
|
|
54
|
+
self.poll_event_queue = dict()
|
|
55
|
+
self.webhook_event_queue = dict()
|
|
56
|
+
self._load_event_queues()
|
|
57
|
+
|
|
58
|
+
def _load_event_queues(self):
|
|
59
|
+
"""Loads event queues from storage."""
|
|
60
|
+
try:
|
|
61
|
+
with open(self.event_queues_file_path, "r") as f:
|
|
62
|
+
queues = EventQueueModel.model_validate_json(f.read())
|
|
63
|
+
|
|
64
|
+
for node in queues.poll.keys():
|
|
65
|
+
for event in queues.poll[node]:
|
|
66
|
+
queue = self.poll_event_queue.setdefault(node, Queue())
|
|
67
|
+
queue.put(event)
|
|
68
|
+
|
|
69
|
+
for node in queues.webhook.keys():
|
|
70
|
+
for event in queues.webhook[node]:
|
|
71
|
+
queue = self.webhook_event_queue.setdefault(node, Queue())
|
|
72
|
+
queue.put(event)
|
|
73
|
+
|
|
74
|
+
except FileNotFoundError:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
def _save_event_queues(self):
|
|
78
|
+
"""Writes event queues to storage."""
|
|
79
|
+
events_model = EventQueueModel(
|
|
80
|
+
poll={
|
|
81
|
+
node: list(queue.queue)
|
|
82
|
+
for node, queue in self.poll_event_queue.items()
|
|
83
|
+
if not queue.empty()
|
|
84
|
+
},
|
|
85
|
+
webhook={
|
|
86
|
+
node: list(queue.queue)
|
|
87
|
+
for node, queue in self.webhook_event_queue.items()
|
|
88
|
+
if not queue.empty()
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if len(events_model.poll) == 0 and len(events_model.webhook) == 0:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
with open(self.event_queues_file_path, "w") as f:
|
|
96
|
+
f.write(events_model.model_dump_json(indent=2))
|
|
97
|
+
|
|
98
|
+
def push_event_to(self, event: Event, node: KoiNetNode, flush=False):
|
|
99
|
+
"""Pushes event to queue of specified node.
|
|
100
|
+
|
|
101
|
+
Event will be sent to webhook or poll queue depending on the node type and edge type of the specified node. If `flush` is set to `True`, the webhook queued will be flushed after pushing the event.
|
|
102
|
+
"""
|
|
103
|
+
logger.debug(f"Pushing event {event.event_type} {event.rid} to {node}")
|
|
104
|
+
|
|
105
|
+
node_profile = self.graph.get_node_profile(node)
|
|
106
|
+
if not node_profile:
|
|
107
|
+
logger.warning(f"Node {node!r} unknown to me")
|
|
108
|
+
|
|
109
|
+
# if there's an edge from me to the target node, override broadcast type
|
|
110
|
+
edge_profile = self.graph.get_edge_profile(
|
|
111
|
+
source=self.identity.rid,
|
|
112
|
+
target=node
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if edge_profile:
|
|
116
|
+
if edge_profile.edge_type == EdgeType.WEBHOOK:
|
|
117
|
+
event_queue = self.webhook_event_queue
|
|
118
|
+
elif edge_profile.edge_type == EdgeType.POLL:
|
|
119
|
+
event_queue = self.poll_event_queue
|
|
120
|
+
else:
|
|
121
|
+
if node_profile.node_type == NodeType.FULL:
|
|
122
|
+
event_queue = self.webhook_event_queue
|
|
123
|
+
elif node_profile.node_type == NodeType.PARTIAL:
|
|
124
|
+
event_queue = self.poll_event_queue
|
|
125
|
+
|
|
126
|
+
queue = event_queue.setdefault(node, Queue())
|
|
127
|
+
queue.put(event)
|
|
128
|
+
|
|
129
|
+
if flush and event_queue is self.webhook_event_queue:
|
|
130
|
+
self.flush_webhook_queue(node)
|
|
131
|
+
|
|
132
|
+
def _flush_queue(self, event_queue: EventQueue, node: KoiNetNode) -> list[Event]:
|
|
133
|
+
"""Flushes a node's queue, returning list of events."""
|
|
134
|
+
queue = event_queue.get(node)
|
|
135
|
+
events = list()
|
|
136
|
+
if queue:
|
|
137
|
+
while not queue.empty():
|
|
138
|
+
event = queue.get()
|
|
139
|
+
logger.debug(f"Dequeued {event.event_type} '{event.rid}'")
|
|
140
|
+
events.append(event)
|
|
141
|
+
|
|
142
|
+
return events
|
|
143
|
+
|
|
144
|
+
def flush_poll_queue(self, node: KoiNetNode) -> list[Event]:
|
|
145
|
+
"""Flushes a node's poll queue, returning list of events."""
|
|
146
|
+
logger.debug(f"Flushing poll queue for {node}")
|
|
147
|
+
return self._flush_queue(self.poll_event_queue, node)
|
|
148
|
+
|
|
149
|
+
def flush_webhook_queue(self, node: KoiNetNode):
|
|
150
|
+
"""Flushes a node's webhook queue, and broadcasts events.
|
|
151
|
+
|
|
152
|
+
If node profile is unknown, or node type is not `FULL`, this operation will fail silently. If the remote node cannot be reached, all events will be requeued.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
logger.debug(f"Flushing webhook queue for {node}")
|
|
156
|
+
|
|
157
|
+
node_profile = self.graph.get_node_profile(node)
|
|
158
|
+
|
|
159
|
+
if not node_profile:
|
|
160
|
+
logger.warning(f"{node!r} not found")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
if node_profile.node_type != NodeType.FULL:
|
|
164
|
+
logger.warning(f"{node!r} is a partial node!")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
events = self._flush_queue(self.webhook_event_queue, node)
|
|
168
|
+
if not events: return
|
|
169
|
+
|
|
170
|
+
logger.debug(f"Broadcasting {len(events)} events")
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
self.request_handler.broadcast_events(node, events=events)
|
|
174
|
+
except httpx.ConnectError:
|
|
175
|
+
logger.warning("Broadcast failed, requeuing events")
|
|
176
|
+
for event in events:
|
|
177
|
+
self.push_event_to(event, node)
|
|
178
|
+
|
|
179
|
+
def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
|
|
180
|
+
"""Returns list of node RIDs which provide state for the specified RID type."""
|
|
181
|
+
|
|
182
|
+
logger.debug(f"Looking for state providers of '{rid_type}'")
|
|
183
|
+
provider_nodes = []
|
|
184
|
+
for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
|
|
185
|
+
node = self.graph.get_node_profile(node_rid)
|
|
186
|
+
|
|
187
|
+
if node.node_type == NodeType.FULL and rid_type in node.provides.state:
|
|
188
|
+
logger.debug(f"Found provider '{node_rid}'")
|
|
189
|
+
provider_nodes.append(node_rid)
|
|
190
|
+
|
|
191
|
+
if not provider_nodes:
|
|
192
|
+
logger.debug("Failed to find providers")
|
|
193
|
+
return provider_nodes
|
|
194
|
+
|
|
195
|
+
def fetch_remote_bundle(self, rid: RID):
|
|
196
|
+
"""Attempts to fetch a bundle by RID from known peer nodes."""
|
|
197
|
+
|
|
198
|
+
logger.debug(f"Fetching remote bundle '{rid}'")
|
|
199
|
+
remote_bundle = None
|
|
200
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
201
|
+
payload = self.request_handler.fetch_bundles(
|
|
202
|
+
node=node_rid, rids=[rid])
|
|
203
|
+
|
|
204
|
+
if payload.bundles:
|
|
205
|
+
remote_bundle = payload.bundles[0]
|
|
206
|
+
logger.debug(f"Got bundle from '{node_rid}'")
|
|
207
|
+
break
|
|
208
|
+
|
|
209
|
+
if not remote_bundle:
|
|
210
|
+
logger.warning("Failed to fetch remote bundle")
|
|
211
|
+
|
|
212
|
+
return remote_bundle
|
|
213
|
+
|
|
214
|
+
def fetch_remote_manifest(self, rid: RID):
|
|
215
|
+
"""Attempts to fetch a manifest by RID from known peer nodes."""
|
|
216
|
+
|
|
217
|
+
logger.debug(f"Fetching remote manifest '{rid}'")
|
|
218
|
+
remote_manifest = None
|
|
219
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
220
|
+
payload = self.request_handler.fetch_manifests(
|
|
221
|
+
node=node_rid, rids=[rid])
|
|
222
|
+
|
|
223
|
+
if payload.manifests:
|
|
224
|
+
remote_manifest = payload.manifests[0]
|
|
225
|
+
logger.debug(f"Got bundle from '{node_rid}'")
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
if not remote_manifest:
|
|
229
|
+
logger.warning("Failed to fetch remote bundle")
|
|
230
|
+
|
|
231
|
+
return remote_manifest
|
|
232
|
+
|
|
233
|
+
def poll_neighbors(self) -> list[Event]:
|
|
234
|
+
"""Polls all neighboring nodes and returns compiled list of events.
|
|
235
|
+
|
|
236
|
+
If this node has no neighbors, it will instead attempt to poll the provided first contact URL.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
neighbors = self.graph.get_neighbors()
|
|
240
|
+
|
|
241
|
+
if not neighbors and self.first_contact:
|
|
242
|
+
logger.debug("No neighbors found, polling first contact")
|
|
243
|
+
try:
|
|
244
|
+
payload = self.request_handler.poll_events(
|
|
245
|
+
url=self.first_contact,
|
|
246
|
+
rid=self.identity.rid
|
|
247
|
+
)
|
|
248
|
+
if payload.events:
|
|
249
|
+
logger.debug(f"Received {len(payload.events)} events from '{self.first_contact}'")
|
|
250
|
+
return payload.events
|
|
251
|
+
except httpx.ConnectError:
|
|
252
|
+
logger.debug(f"Failed to reach first contact '{self.first_contact}'")
|
|
253
|
+
|
|
254
|
+
events = []
|
|
255
|
+
for node_rid in neighbors:
|
|
256
|
+
node = self.graph.get_node_profile(node_rid)
|
|
257
|
+
if not node: continue
|
|
258
|
+
if node.node_type != NodeType.FULL: continue
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
payload = self.request_handler.poll_events(
|
|
262
|
+
node=node_rid,
|
|
263
|
+
rid=self.identity.rid
|
|
264
|
+
)
|
|
265
|
+
if payload.events:
|
|
266
|
+
logger.debug(f"Received {len(payload.events)} events from {node_rid!r}")
|
|
267
|
+
events.extend(payload.events)
|
|
268
|
+
except httpx.ConnectError:
|
|
269
|
+
logger.debug(f"Failed to reach node '{node_rid}'")
|
|
270
|
+
continue
|
|
271
|
+
|
|
272
|
+
return events
|
|
273
|
+
|
|
276
274
|
|