astreum 0.3.16__py3-none-any.whl → 0.3.48__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 (65) 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 +226 -138
  7. astreum/communication/handlers/object_response.py +118 -10
  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/machine/evaluations/low_evaluation.py +5 -5
  24. astreum/machine/models/expression.py +5 -5
  25. astreum/node.py +96 -87
  26. astreum/storage/actions/get.py +285 -183
  27. astreum/storage/actions/set.py +171 -156
  28. astreum/storage/models/atom.py +0 -14
  29. astreum/storage/models/trie.py +2 -2
  30. astreum/storage/providers.py +24 -0
  31. astreum/storage/requests.py +13 -10
  32. astreum/storage/setup.py +20 -15
  33. astreum/utils/config.py +260 -43
  34. astreum/utils/logging.py +1 -1
  35. astreum/{consensus → validation}/__init__.py +0 -4
  36. astreum/validation/constants.py +2 -0
  37. astreum/{consensus → validation}/genesis.py +4 -6
  38. astreum/{consensus → validation}/models/account.py +1 -1
  39. astreum/validation/models/block.py +544 -0
  40. astreum/validation/models/fork.py +511 -0
  41. astreum/{consensus → validation}/models/receipt.py +18 -5
  42. astreum/{consensus → validation}/models/transaction.py +50 -8
  43. astreum/validation/node.py +190 -0
  44. astreum/{consensus → validation}/validator.py +1 -1
  45. astreum/validation/workers/__init__.py +8 -0
  46. astreum/{consensus → validation}/workers/validation.py +360 -333
  47. astreum/verification/__init__.py +4 -0
  48. astreum/{consensus/workers/discovery.py → verification/discover.py} +1 -1
  49. astreum/verification/node.py +61 -0
  50. astreum/verification/worker.py +183 -0
  51. {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/METADATA +45 -9
  52. astreum-0.3.48.dist-info/RECORD +79 -0
  53. astreum/consensus/models/block.py +0 -364
  54. astreum/consensus/models/chain.py +0 -66
  55. astreum/consensus/models/fork.py +0 -100
  56. astreum/consensus/setup.py +0 -83
  57. astreum/consensus/start.py +0 -67
  58. astreum/consensus/workers/__init__.py +0 -9
  59. astreum/consensus/workers/verify.py +0 -90
  60. astreum-0.3.16.dist-info/RECORD +0 -72
  61. /astreum/{consensus → validation}/models/__init__.py +0 -0
  62. /astreum/{consensus → validation}/models/accounts.py +0 -0
  63. {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/WHEEL +0 -0
  64. {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/licenses/LICENSE +0 -0
  65. {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/top_level.txt +0 -0
astreum/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- from astreum.consensus import Account, Accounts, Block, Chain, Fork, Receipt, Transaction
2
+ from astreum.validation import Account, Accounts, Block, Fork, Receipt, Transaction
3
3
  from astreum.machine import Env, Expr, parse, tokenize
4
4
  from astreum.node import Node
5
5
 
@@ -9,7 +9,6 @@ __all__: list[str] = [
9
9
  "Env",
10
10
  "Expr",
11
11
  "Block",
12
- "Chain",
13
12
  "Fork",
14
13
  "Receipt",
15
14
  "Transaction",
@@ -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,62 +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
- existing_peer = node.get_peer(sender_public_key_bytes)
35
- if existing_peer is not None:
36
- existing_peer.address = peer_address
37
- return False
38
-
39
- try:
40
- peer = Peer(
41
- node_secret_key=node.relay_secret_key,
42
- peer_public_key=sender_key,
43
- address=peer_address,
44
- )
45
- except Exception:
46
- return True
47
-
48
- node.add_peer(sender_public_key_bytes, peer)
49
- node.peer_route.add_peer(sender_public_key_bytes, peer)
50
-
51
- node.logger.info(
52
- "Handshake accepted from %s:%s; peer added",
53
- peer_address[0],
54
- peer_address[1],
55
- )
56
- response = Message(
57
- handshake=True,
58
- sender=node.relay_public_key,
59
- content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
60
- )
61
- node.outgoing_queue.put((response.to_bytes(), peer_address))
62
- return True
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