astreum 0.3.16__py3-none-any.whl → 0.3.46__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.
Files changed (60) hide show
  1. astreum/__init__.py +1 -2
  2. astreum/communication/__init__.py +15 -11
  3. astreum/communication/difficulty.py +39 -0
  4. astreum/communication/disconnect.py +57 -0
  5. astreum/communication/handlers/handshake.py +105 -62
  6. astreum/communication/handlers/object_request.py +179 -149
  7. astreum/communication/handlers/object_response.py +7 -1
  8. astreum/communication/handlers/ping.py +9 -0
  9. astreum/communication/handlers/route_request.py +7 -1
  10. astreum/communication/handlers/route_response.py +7 -1
  11. astreum/communication/incoming_queue.py +96 -0
  12. astreum/communication/message_pow.py +36 -0
  13. astreum/communication/models/peer.py +4 -0
  14. astreum/communication/models/ping.py +27 -6
  15. astreum/communication/models/route.py +4 -0
  16. astreum/communication/{start.py → node.py} +10 -11
  17. astreum/communication/outgoing_queue.py +108 -0
  18. astreum/communication/processors/incoming.py +110 -37
  19. astreum/communication/processors/outgoing.py +35 -2
  20. astreum/communication/processors/peer.py +133 -58
  21. astreum/communication/setup.py +272 -113
  22. astreum/communication/util.py +14 -0
  23. astreum/node.py +99 -92
  24. astreum/storage/actions/get.py +79 -48
  25. astreum/storage/actions/set.py +171 -156
  26. astreum/storage/providers.py +24 -0
  27. astreum/storage/setup.py +23 -22
  28. astreum/utils/config.py +234 -45
  29. astreum/utils/logging.py +1 -1
  30. astreum/{consensus → validation}/__init__.py +0 -4
  31. astreum/validation/constants.py +2 -0
  32. astreum/{consensus → validation}/genesis.py +4 -6
  33. astreum/validation/models/block.py +544 -0
  34. astreum/validation/models/fork.py +511 -0
  35. astreum/{consensus → validation}/models/receipt.py +17 -4
  36. astreum/{consensus → validation}/models/transaction.py +45 -3
  37. astreum/validation/node.py +190 -0
  38. astreum/{consensus → validation}/validator.py +1 -1
  39. astreum/validation/workers/__init__.py +8 -0
  40. astreum/{consensus → validation}/workers/validation.py +360 -333
  41. astreum/verification/__init__.py +4 -0
  42. astreum/{consensus/workers/discovery.py → verification/discover.py} +1 -1
  43. astreum/verification/node.py +61 -0
  44. astreum/verification/worker.py +183 -0
  45. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/METADATA +43 -9
  46. astreum-0.3.46.dist-info/RECORD +79 -0
  47. astreum/consensus/models/block.py +0 -364
  48. astreum/consensus/models/chain.py +0 -66
  49. astreum/consensus/models/fork.py +0 -100
  50. astreum/consensus/setup.py +0 -83
  51. astreum/consensus/start.py +0 -67
  52. astreum/consensus/workers/__init__.py +0 -9
  53. astreum/consensus/workers/verify.py +0 -90
  54. astreum-0.3.16.dist-info/RECORD +0 -72
  55. /astreum/{consensus → validation}/models/__init__.py +0 -0
  56. /astreum/{consensus → validation}/models/account.py +0 -0
  57. /astreum/{consensus → validation}/models/accounts.py +0 -0
  58. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/WHEEL +0 -0
  59. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/licenses/LICENSE +0 -0
  60. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from queue import Empty
3
4
  from typing import TYPE_CHECKING, Tuple
4
5
 
5
6
  if TYPE_CHECKING:
@@ -7,14 +8,46 @@ if TYPE_CHECKING:
7
8
 
8
9
  def process_outgoing_messages(node: "Node") -> None:
9
10
  """Send queued outbound packets."""
10
- while True:
11
+ stop = getattr(node, "communication_stop_event", None)
12
+ while stop is None or not stop.is_set():
11
13
  try:
12
- payload, addr = node.outgoing_queue.get()
14
+ item = node.outgoing_queue.get(timeout=0.5)
15
+ except Empty:
16
+ continue
13
17
  except Exception:
14
18
  node.logger.exception("Error taking from outgoing queue")
15
19
  continue
16
20
 
21
+ payload = None
22
+ addr = None
23
+ accounted_size = None
24
+ if isinstance(item, tuple) and len(item) == 3:
25
+ payload, addr, accounted_size = item
26
+ elif isinstance(item, tuple) and len(item) == 2:
27
+ payload, addr = item
28
+ else:
29
+ node.logger.warning("Outgoing queue item has unexpected shape: %r", item)
30
+ continue
31
+
32
+ if stop is not None and stop.is_set():
33
+ if accounted_size is not None:
34
+ try:
35
+ with node.outgoing_queue_size_lock:
36
+ node.outgoing_queue_size = max(0, node.outgoing_queue_size - int(accounted_size))
37
+ except Exception:
38
+ node.logger.exception("Failed updating outgoing_queue_size on shutdown")
39
+ break
40
+
17
41
  try:
18
42
  node.outgoing_socket.sendto(payload, addr)
19
43
  except Exception as exc:
20
44
  node.logger.warning("Error sending message to %s: %s", addr, exc)
45
+ finally:
46
+ if accounted_size is not None:
47
+ try:
48
+ with node.outgoing_queue_size_lock:
49
+ node.outgoing_queue_size = max(0, node.outgoing_queue_size - int(accounted_size))
50
+ except Exception:
51
+ node.logger.exception("Failed updating outgoing_queue_size")
52
+
53
+ node.logger.info("Outgoing message processor stopped")
@@ -1,59 +1,134 @@
1
- from __future__ import annotations
2
-
3
- import time
4
- from datetime import datetime, timedelta, timezone
5
- from typing import TYPE_CHECKING, Any
6
-
7
- if TYPE_CHECKING:
8
- from .. import Node
9
-
10
-
11
- def manage_peer(node: "Node") -> None:
12
- """Continuously evict peers whose timestamps exceed the configured timeout."""
13
- node.logger.info(
14
- "Peer manager started (timeout=%3ds, interval=%3ds)",
15
- node.config["peer_timeout"],
16
- node.config["peer_timeout_interval"],
17
- )
18
- while True:
19
- timeout_seconds = node.config["peer_timeout"]
20
- interval_seconds = node.config["peer_timeout_interval"]
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from datetime import datetime, timedelta, timezone
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from ..models.message import Message
8
+ from ..outgoing_queue import enqueue_outgoing
9
+ from ..util import address_str_to_host_and_port
10
+
11
+ if TYPE_CHECKING:
12
+ from .. import Node
13
+
14
+
15
+ def _queue_bootstrap_handshakes(node: "Node") -> int:
16
+ relay_public_key = node.relay_public_key
17
+
18
+ bootstrap_peers = node.bootstrap_peers
19
+ if not bootstrap_peers:
20
+ return 0
21
+
22
+ try:
23
+ incoming_port = int(node.config.get("incoming_port", 0))
24
+ content = incoming_port.to_bytes(2, "big", signed=False)
25
+ except (TypeError, ValueError, OverflowError):
26
+ return 0
27
+
28
+ handshake_message = Message(
29
+ handshake=True,
30
+ sender=relay_public_key,
31
+ content=content,
32
+ )
33
+ handshake_bytes = handshake_message.to_bytes()
34
+ sent = 0
35
+ for addr in bootstrap_peers:
36
+ try:
37
+ host, port = address_str_to_host_and_port(addr)
38
+ except Exception as exc:
39
+ node.logger.warning("Invalid bootstrap address %s: %s", addr, exc)
40
+ continue
21
41
  try:
22
- peers = getattr(node, "peers", None)
23
- peer_route = getattr(node, "peer_route", None)
24
- if not isinstance(peers, dict) or peer_route is None:
25
- time.sleep(interval_seconds)
26
- continue
27
-
28
- cutoff = datetime.now(timezone.utc) - timedelta(seconds=timeout_seconds)
29
- stale_keys = []
30
- with node.peers_lock:
31
- for peer_key, peer in list(peers.items()):
32
- if peer.timestamp < cutoff:
33
- stale_keys.append(peer_key)
34
-
35
- removed_count = 0
36
- for peer_key in stale_keys:
37
- removed = node.remove_peer(peer_key)
38
- if removed is None:
39
- continue
40
- removed_count += 1
41
- try:
42
- peer_route.remove_peer(peer_key)
43
- except Exception:
44
- node.logger.debug(
45
- "Unable to remove peer %s from route",
46
- peer_key.hex(),
47
- )
48
- node.logger.debug(
49
- "Evicted stale peer %s last seen at %s",
50
- peer_key.hex(),
51
- getattr(removed, "timestamp", None),
52
- )
53
-
54
- if removed_count:
55
- node.logger.info("Peer manager removed %s stale peer(s)", removed_count)
56
- except Exception:
57
- node.logger.exception("Peer manager iteration failed")
58
-
59
- time.sleep(interval_seconds)
42
+ queued = enqueue_outgoing(
43
+ node,
44
+ (host, port),
45
+ message_bytes=handshake_bytes,
46
+ difficulty=1,
47
+ )
48
+ except Exception as exc:
49
+ node.logger.debug(
50
+ "Failed queueing bootstrap handshake to %s:%s: %s",
51
+ host,
52
+ port,
53
+ exc,
54
+ )
55
+ continue
56
+ if queued:
57
+ node.logger.info("Retrying bootstrap handshake to %s:%s", host, port)
58
+ sent += 1
59
+ else:
60
+ node.logger.debug(
61
+ "Bootstrap handshake queue rejected for %s:%s",
62
+ host,
63
+ port,
64
+ )
65
+ return sent
66
+
67
+
68
+ def manage_peer(node: "Node") -> None:
69
+ """Continuously evict peers whose timestamps exceed the configured timeout."""
70
+ node.logger.info(
71
+ "Peer manager started (timeout=%3ds, interval=%3ds)",
72
+ node.config["peer_timeout"],
73
+ node.config["peer_timeout_interval"],
74
+ )
75
+ stop = getattr(node, "communication_stop_event", None)
76
+ while stop is None or not stop.is_set():
77
+ timeout_seconds = node.config["peer_timeout"]
78
+ interval_seconds = node.config["peer_timeout_interval"]
79
+ try:
80
+ peers = getattr(node, "peers", None)
81
+ peer_route = getattr(node, "peer_route", None)
82
+ if not isinstance(peers, dict) or peer_route is None:
83
+ time.sleep(interval_seconds)
84
+ continue
85
+
86
+ cutoff = datetime.now(timezone.utc) - timedelta(seconds=timeout_seconds)
87
+ stale_keys = []
88
+ with node.peers_lock:
89
+ for peer_key, peer in list(peers.items()):
90
+ if peer.timestamp < cutoff:
91
+ stale_keys.append(peer_key)
92
+
93
+ removed_count = 0
94
+ for peer_key in stale_keys:
95
+ removed = node.remove_peer(peer_key)
96
+ if removed is None:
97
+ continue
98
+ removed_count += 1
99
+ try:
100
+ peer_route.remove_peer(peer_key)
101
+ except Exception:
102
+ node.logger.debug(
103
+ "Unable to remove peer %s from route",
104
+ peer_key.hex(),
105
+ )
106
+ node.logger.debug(
107
+ "Evicted stale peer %s last seen at %s",
108
+ peer_key.hex(),
109
+ getattr(removed, "timestamp", None),
110
+ )
111
+
112
+ if removed_count:
113
+ node.logger.info("Peer manager removed %s stale peer(s)", removed_count)
114
+
115
+ try:
116
+ with node.peers_lock:
117
+ peer_count = len(peers)
118
+ except Exception:
119
+ peer_count = len(getattr(node, "peers", {}) or {})
120
+ if peer_count == 0:
121
+ bootstrap_interval = node.config.get("bootstrap_retry_interval", 0)
122
+ now = time.time()
123
+ last_attempt = getattr(node, "_bootstrap_last_attempt", 0.0)
124
+ if bootstrap_interval and (now - last_attempt) >= bootstrap_interval:
125
+ sent = _queue_bootstrap_handshakes(node)
126
+ if sent:
127
+ node._bootstrap_last_attempt = now
128
+ except Exception:
129
+ node.logger.exception("Peer manager iteration failed")
130
+
131
+ if stop is not None and stop.wait(interval_seconds):
132
+ break
133
+
134
+ node.logger.info("Peer manager stopped")