astreum 0.3.9__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 +5 -4
  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 -89
  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 +134 -0
  21. astreum/communication/setup.py +273 -112
  22. astreum/communication/util.py +14 -0
  23. astreum/node.py +99 -89
  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 +247 -30
  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 +18 -9
  39. astreum/validation/workers/__init__.py +8 -0
  40. astreum/{consensus → validation}/workers/validation.py +361 -307
  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.9.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.9.dist-info/RECORD +0 -71
  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.9.dist-info → astreum-0.3.46.dist-info}/WHEEL +0 -0
  59. {astreum-0.3.9.dist-info → astreum-0.3.46.dist-info}/licenses/LICENSE +0 -0
  60. {astreum-0.3.9.dist-info → astreum-0.3.46.dist-info}/top_level.txt +0 -0
astreum/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
-
2
- from astreum.consensus import Account, Accounts, Block, Chain, Fork, Receipt, Transaction
3
- from astreum.machine import Env, Expr
1
+
2
+ from astreum.validation import Account, Accounts, Block, Fork, Receipt, Transaction
3
+ from astreum.machine import Env, Expr, parse, tokenize
4
4
  from astreum.node import Node
5
5
 
6
6
 
@@ -9,10 +9,11 @@ __all__: list[str] = [
9
9
  "Env",
10
10
  "Expr",
11
11
  "Block",
12
- "Chain",
13
12
  "Fork",
14
13
  "Receipt",
15
14
  "Transaction",
16
15
  "Account",
17
16
  "Accounts",
17
+ "parse",
18
+ "tokenize",
18
19
  ]
@@ -1,11 +1,15 @@
1
- from .models.message import Message
2
- from .models.peer import Peer
3
- from .models.route import Route
4
- from .setup import communication_setup
5
-
6
- __all__ = [
7
- "Message",
8
- "Peer",
9
- "Route",
10
- "communication_setup",
11
- ]
1
+ from .models.message import Message
2
+ from .models.peer import Peer
3
+ from .models.route import Route
4
+ from .incoming_queue import enqueue_incoming
5
+ from .outgoing_queue import enqueue_outgoing
6
+ from .setup import communication_setup
7
+
8
+ __all__ = [
9
+ "Message",
10
+ "Peer",
11
+ "Route",
12
+ "enqueue_incoming",
13
+ "enqueue_outgoing",
14
+ "communication_setup",
15
+ ]
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from .. import Node
7
+
8
+
9
+ def message_difficulty(node: "Node") -> int:
10
+ """Compute current message difficulty based on incoming queue pressure."""
11
+ size = node.incoming_queue_size
12
+ limit = node.incoming_queue_size_limit
13
+
14
+ if limit <= 0:
15
+ return 1
16
+
17
+ pressure = size / limit
18
+ if pressure < 0.70:
19
+ value = 1
20
+ elif pressure < 0.75:
21
+ value = 3
22
+ elif pressure < 0.80:
23
+ value = 5
24
+ elif pressure < 0.85:
25
+ value = 8
26
+ elif pressure < 0.90:
27
+ value = 12
28
+ elif pressure < 0.93:
29
+ value = 16
30
+ elif pressure < 0.95:
31
+ value = 19
32
+ elif pressure < 0.97:
33
+ value = 22
34
+ elif pressure < 0.98:
35
+ value = 24
36
+ else:
37
+ value = 26
38
+
39
+ return max(1, min(255, value))
@@ -0,0 +1,57 @@
1
+ """Helpers related to disconnecting communication components."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from astreum.node import Node
9
+
10
+
11
+ _SOCKET_ATTRS: tuple[str, ...] = ("incoming_socket", "outgoing_socket")
12
+ _THREAD_ATTRS: tuple[str, ...] = (
13
+ "incoming_populate_thread",
14
+ "incoming_process_thread",
15
+ "outgoing_thread",
16
+ "peer_manager_thread",
17
+ "latest_block_discovery_thread",
18
+ "verify_thread",
19
+ "consensus_validation_thread",
20
+ )
21
+
22
+
23
+ def _set_event(node: "Node", attr_name: str) -> None:
24
+ event = getattr(node, attr_name, None)
25
+ if event is not None:
26
+ event.set()
27
+
28
+
29
+ def _close_socket(node: "Node", attr_name: str) -> None:
30
+ sock = getattr(node, attr_name, None)
31
+ if sock is None:
32
+ return
33
+ try:
34
+ sock.close()
35
+ except Exception as exc: # pragma: no cover - defensive logging path
36
+ node.logger.warning("Error closing %s: %s", attr_name, exc)
37
+
38
+
39
+ def disconnect_node(node: "Node") -> None:
40
+ """Gracefully stop worker threads and close communication sockets."""
41
+ node.logger.info("Disconnecting Astreum Node")
42
+
43
+ _set_event(node, "communication_stop_event")
44
+ _set_event(node, "_validation_stop_event")
45
+ _set_event(node, "_verify_stop_event")
46
+
47
+ for sock_name in _SOCKET_ATTRS:
48
+ _close_socket(node, sock_name)
49
+
50
+ for thread_name in _THREAD_ATTRS:
51
+ thread = getattr(node, thread_name, None)
52
+ if thread is None or not thread.is_alive():
53
+ continue
54
+ thread.join(timeout=1.0)
55
+
56
+ node.is_connected = False
57
+ node.logger.info("Node disconnected")
@@ -1,89 +1,105 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Sequence
4
-
5
- from cryptography.hazmat.primitives import serialization
6
- from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
7
-
8
- from ..models.peer import Peer
9
- from ..models.message import Message
10
-
11
- if TYPE_CHECKING:
12
- from .... import Node
13
-
14
-
15
- def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
16
- """Handle incoming handshake messages.
17
-
18
- Returns True if the outer loop should `continue`, False otherwise.
19
- """
20
- sender_public_key_bytes = message.sender_bytes
21
- try:
22
- sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
23
- except Exception as exc:
24
- node.logger.warning("Error extracting sender key bytes: %s", exc)
25
- return True
26
-
27
- try:
28
- host = addr[0]
29
- port = int.from_bytes(message.content[:2], "big", signed=False)
30
- except Exception:
31
- return True
32
- peer_address = (host, port)
33
-
34
- old_key_bytes = node.addresses.get(peer_address)
35
- node.addresses[peer_address] = sender_public_key_bytes
36
-
37
- if old_key_bytes is None:
38
- try:
39
- peer = Peer(
40
- node_secret_key=node.relay_secret_key,
41
- peer_public_key=sender_key,
42
- address=peer_address,
43
- )
44
- except Exception:
45
- return True
46
-
47
- node.add_peer(sender_public_key_bytes, peer)
48
- node.peer_route.add_peer(sender_public_key_bytes, peer)
49
-
50
- node.logger.info(
51
- "Handshake accepted from %s:%s; peer added",
52
- peer_address[0],
53
- peer_address[1],
54
- )
55
- response = Message(
56
- handshake=True,
57
- sender=node.relay_public_key,
58
- content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
59
- )
60
- node.outgoing_queue.put((response.to_bytes(), peer_address))
61
- return True
62
-
63
- if old_key_bytes == sender_public_key_bytes:
64
- peer = node.get_peer(sender_public_key_bytes)
65
- if peer is not None:
66
- peer.address = peer_address
67
- return False
68
-
69
- try:
70
- node.peer_route.remove_peer(old_key_bytes)
71
- except Exception:
72
- pass
73
- try:
74
- peer = Peer(
75
- node_secret_key=node.relay_secret_key,
76
- peer_public_key=sender_key,
77
- address=peer_address,
78
- )
79
- except Exception:
80
- return True
81
-
82
- node.replace_peer(old_key_bytes, sender_public_key_bytes, peer)
83
- node.peer_route.add_peer(sender_public_key_bytes, peer)
84
- node.logger.info(
85
- "Peer at %s:%s replaced due to key change",
86
- peer_address[0],
87
- peer_address[1],
88
- )
89
- return False
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Sequence
4
+
5
+ from cryptography.hazmat.primitives import serialization
6
+ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
7
+
8
+ from ..outgoing_queue import enqueue_outgoing
9
+ from ..models.peer import Peer
10
+ from ..models.message import Message, MessageTopic
11
+ from ..models.ping import Ping
12
+ from ..difficulty import message_difficulty
13
+
14
+ if TYPE_CHECKING:
15
+ from .... import Node
16
+
17
+
18
+ def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
19
+ """Handle incoming handshake messages.
20
+
21
+ Returns True if the outer loop should `continue`, False otherwise.
22
+ """
23
+ def _queue_handshake_ping(peer: Peer, peer_address: tuple[str, int]) -> None:
24
+ latest_block = getattr(node, "latest_block_hash", None)
25
+ if not isinstance(latest_block, (bytes, bytearray)) or len(latest_block) != 32:
26
+ latest_block = None
27
+ try:
28
+ ping_payload = Ping(
29
+ is_validator=bool(getattr(node, "validation_public_key", None)),
30
+ difficulty=message_difficulty(node),
31
+ latest_block=latest_block,
32
+ ).to_bytes()
33
+ ping_msg = Message(
34
+ topic=MessageTopic.PING,
35
+ content=ping_payload,
36
+ sender=node.relay_public_key,
37
+ )
38
+ ping_msg.encrypt(peer.shared_key_bytes)
39
+ enqueue_outgoing(
40
+ node,
41
+ peer_address,
42
+ message=ping_msg,
43
+ difficulty=peer.difficulty,
44
+ )
45
+ except Exception as exc:
46
+ node.logger.debug(
47
+ "Failed sending handshake ping to %s:%s: %s",
48
+ peer_address[0],
49
+ peer_address[1],
50
+ exc,
51
+ )
52
+ sender_public_key_bytes = message.sender_bytes
53
+ try:
54
+ sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
55
+ except Exception as exc:
56
+ node.logger.warning("Error extracting sender key bytes: %s", exc)
57
+ return True
58
+
59
+ try:
60
+ host = addr[0]
61
+ port = int.from_bytes(message.content[:2], "big", signed=False)
62
+ except Exception:
63
+ return True
64
+ peer_address = (host, port)
65
+ default_seed_ips = getattr(node, "default_seed_ips", None)
66
+ is_default_seed = bool(default_seed_ips) and host in default_seed_ips
67
+
68
+ existing_peer = node.get_peer(sender_public_key_bytes)
69
+ if existing_peer is not None:
70
+ existing_peer.address = peer_address
71
+ existing_peer.is_default_seed = is_default_seed
72
+ _queue_handshake_ping(existing_peer, peer_address)
73
+ return False
74
+
75
+ try:
76
+ peer = Peer(
77
+ node_secret_key=node.relay_secret_key,
78
+ peer_public_key=sender_key,
79
+ address=peer_address,
80
+ is_default_seed=is_default_seed,
81
+ )
82
+ except Exception:
83
+ return True
84
+
85
+ node.add_peer(sender_public_key_bytes, peer)
86
+ node.peer_route.add_peer(sender_public_key_bytes, peer)
87
+
88
+ node.logger.info(
89
+ "Handshake accepted from %s:%s; peer added",
90
+ peer_address[0],
91
+ peer_address[1],
92
+ )
93
+ response = Message(
94
+ handshake=True,
95
+ sender=node.relay_public_key,
96
+ content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
97
+ )
98
+ enqueue_outgoing(
99
+ node,
100
+ peer_address,
101
+ message=response,
102
+ difficulty=peer.difficulty,
103
+ )
104
+ _queue_handshake_ping(peer, peer_address)
105
+ return True