elo-node 0.4.3__tar.gz → 0.4.4__tar.gz
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.
- {elo_node-0.4.3 → elo_node-0.4.4}/PKG-INFO +1 -1
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/__init__.py +1 -1
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/node.py +77 -4
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/transport/protocol.py +6 -2
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/PKG-INFO +1 -1
- {elo_node-0.4.3 → elo_node-0.4.4}/pyproject.toml +1 -1
- {elo_node-0.4.3 → elo_node-0.4.4}/tests/test_unit.py +1 -1
- {elo_node-0.4.3 → elo_node-0.4.4}/README.md +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/__main__.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/security.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/transport/__init__.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/transport/routing.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/transport/tcp.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/transport/tracker.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo/types.py +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/SOURCES.txt +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/dependency_links.txt +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/entry_points.txt +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/requires.txt +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/elo_node.egg-info/top_level.txt +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/setup.cfg +0 -0
- {elo_node-0.4.3 → elo_node-0.4.4}/tests/test_security.py +0 -0
|
@@ -78,7 +78,7 @@ class Node:
|
|
|
78
78
|
peers: list[str] | None = None,
|
|
79
79
|
tracker: str = "public",
|
|
80
80
|
allowlist: list[str] | None = None,
|
|
81
|
-
version: str = "0.4.
|
|
81
|
+
version: str = "0.4.4",
|
|
82
82
|
identity: EphemeralIdentity | None = None,
|
|
83
83
|
verify_peers: bool = True,
|
|
84
84
|
heartbeat_interval_s: int = 30,
|
|
@@ -253,6 +253,10 @@ class Node:
|
|
|
253
253
|
except Exception as e:
|
|
254
254
|
return Result.make_error(task_id, "SEND_ERROR", str(e))
|
|
255
255
|
|
|
256
|
+
# Bug 2: Fallback via tracker antes de desistir
|
|
257
|
+
if not peer:
|
|
258
|
+
return await self.send_task_via_tracker("", target_node, capability, payload, ttl_s=ttl_s)
|
|
259
|
+
|
|
256
260
|
return Result.make_error(task_id, "NO_PEER", f"No peer for: {capability}")
|
|
257
261
|
|
|
258
262
|
async def send_task_async(self, target_node: str, capability: str,
|
|
@@ -292,10 +296,11 @@ class Node:
|
|
|
292
296
|
|
|
293
297
|
# ── descoberta ─────────────────────────────────────────────
|
|
294
298
|
|
|
295
|
-
async def
|
|
296
|
-
"""Return all known peers with capabilities.
|
|
299
|
+
async def get_known_peers_local(self) -> list[dict[str, Any]]:
|
|
300
|
+
"""Return all locally-known peers with capabilities (no network discovery).
|
|
297
301
|
|
|
298
302
|
Merges data from TCP connections (live) and InterestTable (registered).
|
|
303
|
+
Does NOT perform any network queries — only returns what's already known.
|
|
299
304
|
"""
|
|
300
305
|
result: dict[str, dict[str, Any]] = {}
|
|
301
306
|
|
|
@@ -314,6 +319,56 @@ class Node:
|
|
|
314
319
|
|
|
315
320
|
return list(result.values())
|
|
316
321
|
|
|
322
|
+
# Alias de compatibilidade — o método anterior chamava-se discover_peers()
|
|
323
|
+
async def discover_peers(self) -> list[dict[str, Any]]:
|
|
324
|
+
"""[DEPRECATED] Use get_known_peers_local() ou discover_peers_network().
|
|
325
|
+
|
|
326
|
+
Este método apenas consolida peers localmente conhecidos (TCP + InterestTable).
|
|
327
|
+
Não faz descoberta ativa de rede. Prefira discover_peers_network() para
|
|
328
|
+
descoberta via broadcast, ou get_known_peers() para peers com handshake completo.
|
|
329
|
+
"""
|
|
330
|
+
return await self.get_known_peers_local()
|
|
331
|
+
|
|
332
|
+
async def discover_peers_network(self, timeout: float = 5.0) -> list[dict[str, Any]]:
|
|
333
|
+
"""Discover peers on the network via QUERY broadcast with empty capability.
|
|
334
|
+
|
|
335
|
+
Sends a broadcast QUERY for an empty capability (catch-all) and aggregates
|
|
336
|
+
responses for the given timeout period. Returns list of discovered peer dicts.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
timeout: seconds to wait for responses (default 5.0)
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
List of dicts with 'addr' and optionally 'node_id'
|
|
343
|
+
"""
|
|
344
|
+
query_id = str(uuid.uuid4())[:8]
|
|
345
|
+
future: asyncio.Future = asyncio.get_event_loop().create_future()
|
|
346
|
+
self._pending_queries[query_id] = (future, time.time())
|
|
347
|
+
discovered: dict[str, dict[str, Any]] = {}
|
|
348
|
+
|
|
349
|
+
# Broadcast QUERY for any capability
|
|
350
|
+
await self._tcp.broadcast(query_msg("", query_id, ttl=3))
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
result_addr = await asyncio.wait_for(future, timeout=timeout)
|
|
354
|
+
if result_addr:
|
|
355
|
+
discovered[result_addr] = {"addr": result_addr, "connected": False, "caps": [], "via": "network"}
|
|
356
|
+
except asyncio.TimeoutError:
|
|
357
|
+
pass
|
|
358
|
+
finally:
|
|
359
|
+
self._pending_queries.pop(query_id, None)
|
|
360
|
+
|
|
361
|
+
# Merge with locally known peers
|
|
362
|
+
local = await self.get_known_peers_local()
|
|
363
|
+
for entry in local:
|
|
364
|
+
addr = entry["addr"]
|
|
365
|
+
if addr not in discovered:
|
|
366
|
+
discovered[addr] = entry
|
|
367
|
+
else:
|
|
368
|
+
discovered[addr].update(entry)
|
|
369
|
+
|
|
370
|
+
return list(discovered.values())
|
|
371
|
+
|
|
317
372
|
def get_known_peers(self) -> list[dict[str, Any]]:
|
|
318
373
|
"""Return peers registered in InterestTable (completed HELLO handshake).
|
|
319
374
|
|
|
@@ -355,6 +410,18 @@ class Node:
|
|
|
355
410
|
await self._on_hello(peer_addr, msg)
|
|
356
411
|
elif msg_type == MessageType.HELLO_ACK:
|
|
357
412
|
await self._on_hello(peer_addr, msg)
|
|
413
|
+
# Bug 1: Se HELLO_ACK contém known_peers, conectar-se a eles
|
|
414
|
+
known_peers = msg.get("known_peers")
|
|
415
|
+
if known_peers:
|
|
416
|
+
hello = hello_msg(self._node_id, self._tracker.get_public_caps(),
|
|
417
|
+
list(self._routing.local_interests),
|
|
418
|
+
self._tracker.visibility, self._version)
|
|
419
|
+
for peer_info in known_peers:
|
|
420
|
+
addr = peer_info.get("addr", "")
|
|
421
|
+
if addr and addr != peer_addr and addr not in self._tcp.peer_addresses:
|
|
422
|
+
asyncio.create_task(
|
|
423
|
+
self._tcp.connect_to_peer(addr, hello_payload=hello)
|
|
424
|
+
)
|
|
358
425
|
elif msg_type == MessageType.QUERY:
|
|
359
426
|
await self._on_query(peer_addr, msg)
|
|
360
427
|
elif msg_type == MessageType.QUERY_RESP:
|
|
@@ -376,8 +443,14 @@ class Node:
|
|
|
376
443
|
interests = msg.get("interests", [])
|
|
377
444
|
self._routing.register_peer(peer_addr, caps, interests)
|
|
378
445
|
|
|
446
|
+
# Se for tracker (public/private), inclui peers conhecidos no ACK
|
|
447
|
+
known_peers = None
|
|
448
|
+
if msg.get("type") == MessageType.HELLO and self._tracker.visibility in ("public", "private"):
|
|
449
|
+
known_peers = self.get_known_peers()
|
|
450
|
+
|
|
379
451
|
ack = hello_ack_msg(self._node_id, self._tracker.get_caps_for_peer(node_id),
|
|
380
|
-
list(self._routing.local_interests), self._tracker.visibility
|
|
452
|
+
list(self._routing.local_interests), self._tracker.visibility,
|
|
453
|
+
known_peers=known_peers)
|
|
381
454
|
try:
|
|
382
455
|
await self._tcp.send_to(peer_addr, ack)
|
|
383
456
|
except Exception:
|
|
@@ -89,14 +89,18 @@ def hello_msg(node_id: str, caps: dict, interests: list[str],
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def hello_ack_msg(node_id: str, caps: dict, interests: list[str],
|
|
92
|
-
tracker: str = "public"
|
|
93
|
-
|
|
92
|
+
tracker: str = "public",
|
|
93
|
+
known_peers: list[dict] | None = None) -> dict:
|
|
94
|
+
msg: dict[str, Any] = {
|
|
94
95
|
"type": MessageType.HELLO_ACK,
|
|
95
96
|
"node_id": node_id,
|
|
96
97
|
"caps": caps,
|
|
97
98
|
"interests": interests,
|
|
98
99
|
"tracker": tracker,
|
|
99
100
|
}
|
|
101
|
+
if known_peers is not None:
|
|
102
|
+
msg["known_peers"] = known_peers
|
|
103
|
+
return msg
|
|
100
104
|
|
|
101
105
|
|
|
102
106
|
def query_msg(capability: str, query_id: str, ttl: int = 5) -> dict:
|
|
@@ -136,7 +136,7 @@ class TestNodeConstruction:
|
|
|
136
136
|
node = Node("test-node")
|
|
137
137
|
assert node._name == "test-node"
|
|
138
138
|
assert node._port == 7878
|
|
139
|
-
assert node._version == "0.4.
|
|
139
|
+
assert node._version == "0.4.4"
|
|
140
140
|
assert node.connected is False
|
|
141
141
|
assert node.node_id is not None
|
|
142
142
|
assert len(node.node_id) > 20
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|