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
@@ -1,148 +1,303 @@
1
- import socket, threading
2
- from queue import Queue
3
- from typing import Tuple, Optional
4
- from cryptography.hazmat.primitives import serialization
5
- from cryptography.hazmat.primitives.asymmetric import ed25519
6
- from cryptography.hazmat.primitives.asymmetric.x25519 import (
7
- X25519PrivateKey,
8
- X25519PublicKey,
9
- )
10
-
11
- from typing import TYPE_CHECKING
12
- if TYPE_CHECKING:
13
- from .. import Node
14
-
15
- from . import Route, Message
1
+ import heapq, os, socket, threading, time
2
+ from pathlib import Path
3
+ from queue import Queue
4
+ from typing import Tuple, Optional, Set
5
+ from cryptography.hazmat.primitives import serialization
6
+ from cryptography.hazmat.primitives.asymmetric import ed25519
7
+ from cryptography.hazmat.primitives.asymmetric.x25519 import (
8
+ X25519PrivateKey,
9
+ X25519PublicKey,
10
+ )
11
+
12
+ from typing import TYPE_CHECKING
13
+ if TYPE_CHECKING:
14
+ from .. import Node
15
+
16
+ from . import Route, Message
16
17
  from .processors.incoming import (
17
18
  process_incoming_messages,
18
19
  populate_incoming_messages,
19
20
  )
20
21
  from .processors.outgoing import process_outgoing_messages
22
+ from .processors.peer import manage_peer
23
+ from .outgoing_queue import enqueue_outgoing
21
24
  from .util import address_str_to_host_and_port
22
25
  from ..utils.bytes import hex_to_bytes
23
-
24
- def load_x25519(hex_key: Optional[str]) -> X25519PrivateKey:
25
- """DH key for relaying (always X25519)."""
26
- if hex_key:
27
- return X25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key))
28
- return X25519PrivateKey.generate()
29
-
30
- def load_ed25519(hex_key: Optional[str]) -> Optional[ed25519.Ed25519PrivateKey]:
31
- """Signing key for validation (Ed25519), or None if absent."""
32
- return ed25519.Ed25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key)) \
33
- if hex_key else None
34
-
35
- def make_routes(
36
- relay_pk: X25519PublicKey,
37
- val_sk: Optional[ed25519.Ed25519PrivateKey]
38
- ) -> Tuple[Route, Optional[Route]]:
39
- """Peer route (DH pubkey) + optional validation route (ed pubkey)."""
40
- peer_rt = Route(relay_pk)
41
- val_rt = Route(val_sk.public_key()) if val_sk else None
42
- return peer_rt, val_rt
43
-
26
+
27
+ def load_x25519(hex_key: Optional[str]) -> X25519PrivateKey:
28
+ """DH key for relaying (always X25519)."""
29
+ if hex_key:
30
+ return X25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key))
31
+ return X25519PrivateKey.generate()
32
+
33
+ def load_ed25519(hex_key: Optional[str]) -> Optional[ed25519.Ed25519PrivateKey]:
34
+ """Signing key for validation (Ed25519), or None if absent."""
35
+ return ed25519.Ed25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key)) \
36
+ if hex_key else None
37
+
38
+ def make_routes(
39
+ relay_pk: X25519PublicKey,
40
+ val_sk: Optional[ed25519.Ed25519PrivateKey]
41
+ ) -> Tuple[Route, Optional[Route]]:
42
+ """Peer route (DH pubkey) + optional validation route (ed pubkey)."""
43
+ peer_rt = Route(relay_pk)
44
+ val_rt = Route(val_sk.public_key()) if val_sk else None
45
+ return peer_rt, val_rt
46
+
44
47
  def make_maps():
45
48
  """Empty lookup maps: peers and addresses."""
46
49
  return
47
50
 
48
51
 
49
- def communication_setup(node: "Node", config: dict):
50
- node.logger.info("Setting up node communication")
51
- node.use_ipv6 = config.get('use_ipv6', False)
52
- node.peers_lock = threading.RLock()
52
+ def _resolve_default_seed_ips(node: "Node", default_seed: Optional[str]) -> Set[str]:
53
+ if default_seed is None:
54
+ return set()
55
+ try:
56
+ host, port = address_str_to_host_and_port(default_seed)
57
+ except Exception as exc:
58
+ node.logger.warning("Invalid default seed %s: %s", default_seed, exc)
59
+ return set()
60
+ try:
61
+ infos = socket.getaddrinfo(host, port, type=socket.SOCK_DGRAM)
62
+ except Exception as exc:
63
+ node.logger.warning("Failed resolving default seed %s:%s: %s", host, port, exc)
64
+ return set()
65
+ resolved = {info[4][0] for info in infos if info[4]}
66
+ if resolved:
67
+ resolved_list = ", ".join(sorted(resolved))
68
+ node.logger.info("Default seed resolved to %s", resolved_list)
69
+ else:
70
+ node.logger.warning("No IPs resolved for default seed %s:%s", host, port)
71
+ return resolved
53
72
 
54
- # key loading
55
- node.relay_secret_key = load_x25519(config.get('relay_secret_key'))
56
- node.validation_secret_key = load_ed25519(config.get('validation_secret_key'))
57
73
 
58
- # derive pubs + routes
59
- node.relay_public_key = node.relay_secret_key.public_key()
60
- node.relay_public_key_bytes = node.relay_public_key.public_bytes(
61
- encoding=serialization.Encoding.Raw,
62
- format=serialization.PublicFormat.Raw,
63
- )
64
- node.validation_public_key = (
65
- node.validation_secret_key.public_key().public_bytes(
66
- encoding=serialization.Encoding.Raw,
67
- format=serialization.PublicFormat.Raw,
74
+ def advertise_cold_storage(node: "Node") -> None:
75
+ """Advertise all cold storage atom ids to the closest known peer."""
76
+ node_logger = node.logger
77
+ cold_path = node.config.get("cold_storage_path")
78
+ if not cold_path:
79
+ node_logger.debug("Cold storage disabled; skipping cold atom advertisement")
80
+ return
81
+ advertise_limit = node.config.get("cold_storage_advertise_limit", 1000)
82
+ if advertise_limit == 0:
83
+ node_logger.debug(
84
+ "Cold storage advertisement disabled; skipping cold atom advertisement"
68
85
  )
69
- if node.validation_secret_key
70
- else None
71
- )
72
- node.peer_route, node.validation_route = make_routes(
73
- node.relay_public_key,
74
- node.validation_secret_key
86
+ return
87
+
88
+ directory = Path(cold_path)
89
+ if not directory.exists():
90
+ node_logger.warning("Cold storage path %s missing; cannot advertise atoms", directory)
91
+ return
92
+ if not directory.is_dir():
93
+ node_logger.warning("Cold storage path %s is not a directory; skipping", directory)
94
+ return
95
+
96
+ advertised = 0
97
+ skipped = 0
98
+ if advertise_limit < 0:
99
+ for file_path in directory.glob("*.bin"):
100
+ if not file_path.is_file():
101
+ skipped += 1
102
+ continue
103
+ atom_hex = file_path.stem
104
+ if len(atom_hex) != 64:
105
+ skipped += 1
106
+ continue
107
+ try:
108
+ atom_id = bytes.fromhex(atom_hex)
109
+ except ValueError:
110
+ skipped += 1
111
+ continue
112
+ if len(atom_id) != 32:
113
+ skipped += 1
114
+ continue
115
+ node._network_set(atom_id)
116
+ advertised += 1
117
+ else:
118
+ heap = []
119
+ for entry in os.scandir(directory):
120
+ name = entry.name
121
+ if not name.endswith(".bin"):
122
+ continue
123
+ if not entry.is_file():
124
+ skipped += 1
125
+ continue
126
+ atom_hex = name[:-4]
127
+ if len(atom_hex) != 64:
128
+ skipped += 1
129
+ continue
130
+ try:
131
+ atom_id = bytes.fromhex(atom_hex)
132
+ except ValueError:
133
+ skipped += 1
134
+ continue
135
+ if len(atom_id) != 32:
136
+ skipped += 1
137
+ continue
138
+ try:
139
+ mtime = entry.stat().st_mtime
140
+ except OSError:
141
+ skipped += 1
142
+ continue
143
+ if len(heap) < advertise_limit:
144
+ heapq.heappush(heap, (mtime, atom_id))
145
+ else:
146
+ if mtime > heap[0][0]:
147
+ heapq.heapreplace(heap, (mtime, atom_id))
148
+ for _, atom_id in sorted(heap, key=lambda item: item[0], reverse=True):
149
+ node._network_set(atom_id)
150
+ advertised += 1
151
+
152
+ node_logger.info(
153
+ "Cold storage advertisement complete (advertised=%s, skipped=%s)",
154
+ advertised,
155
+ skipped,
75
156
  )
76
157
 
77
- # connection state & atom request tracking
78
- node.is_connected = False
79
- node.atom_requests = set()
80
- node.atom_requests_lock = threading.RLock()
81
158
 
82
- # sockets + queues + threads
83
- incoming_port = config.get('incoming_port', 7373)
159
+ def manage_storage_index(node: "Node") -> None:
160
+ interval = node.config.get("storage_index_interval", 0)
161
+ if not interval:
162
+ node.logger.info("Storage index advertiser disabled")
163
+ return
164
+ node.logger.info("Storage index advertiser started (interval=%ss)", interval)
165
+ stop = getattr(node, "communication_stop_event", None)
166
+ while stop is None or not stop.is_set():
167
+ if stop is not None and stop.wait(interval):
168
+ break
169
+ try:
170
+ advertise_cold_storage(node)
171
+ except Exception:
172
+ node.logger.exception("Storage index advertisement failed")
173
+ node.logger.info("Storage index advertiser stopped")
174
+
175
+
176
+ def communication_setup(node: "Node", config: dict):
177
+ node.logger.info("Setting up node communication")
178
+ node.use_ipv6 = config.get('use_ipv6', False)
179
+ node.peers_lock = threading.RLock()
180
+ node.communication_stop_event = threading.Event()
181
+ default_seed = config.get("default_seed")
182
+ node.default_seed_ips = _resolve_default_seed_ips(node, default_seed)
183
+
184
+ # key loading
185
+ node.relay_secret_key = load_x25519(config.get('relay_secret_key'))
186
+ node.validation_secret_key = load_ed25519(config.get('validation_secret_key'))
187
+
188
+ # derive pubs + routes
189
+ node.relay_public_key = node.relay_secret_key.public_key()
190
+ node.relay_public_key_bytes = node.relay_public_key.public_bytes(
191
+ encoding=serialization.Encoding.Raw,
192
+ format=serialization.PublicFormat.Raw,
193
+ )
194
+ node.validation_public_key = (
195
+ node.validation_secret_key.public_key().public_bytes(
196
+ encoding=serialization.Encoding.Raw,
197
+ format=serialization.PublicFormat.Raw,
198
+ )
199
+ if node.validation_secret_key
200
+ else None
201
+ )
202
+ node.peer_route, node.validation_route = make_routes(
203
+ node.relay_public_key,
204
+ node.validation_secret_key
205
+ )
206
+
207
+ # connection state & atom request tracking
208
+ node.is_connected = False
209
+ node.atom_requests = set()
210
+ node.atom_requests_lock = threading.RLock()
211
+
212
+ # sockets + queues + threads
213
+ incoming_port = config.get("incoming_port")
214
+ if incoming_port is None:
215
+ raise ValueError("incoming_port must be configured before communication setup")
84
216
  fam = socket.AF_INET6 if node.use_ipv6 else socket.AF_INET
85
217
  node.incoming_socket = socket.socket(fam, socket.SOCK_DGRAM)
86
218
  if node.use_ipv6:
87
219
  node.incoming_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
88
- node.incoming_socket.bind(("::" if node.use_ipv6 else "0.0.0.0", incoming_port or 0))
89
- node.incoming_port = node.incoming_socket.getsockname()[1]
220
+ node.incoming_socket.bind(("::" if node.use_ipv6 else "0.0.0.0", incoming_port))
221
+ bound_port = node.incoming_socket.getsockname()[1]
222
+ if incoming_port != 0 and bound_port != incoming_port:
223
+ raise OSError(
224
+ f"incoming_port mismatch: requested {incoming_port}, got {bound_port}"
225
+ )
226
+ node.config["incoming_port"] = bound_port if incoming_port == 0 else incoming_port
227
+ node.incoming_socket.settimeout(0.5)
90
228
  node.logger.info(
91
229
  "Incoming UDP socket bound to %s:%s",
92
230
  "::" if node.use_ipv6 else "0.0.0.0",
93
- node.incoming_port,
231
+ node.config["incoming_port"],
94
232
  )
95
233
  node.incoming_queue = Queue()
234
+ node.incoming_queue_size = 0
235
+ node.incoming_queue_size_lock = threading.RLock()
236
+ node.incoming_queue_size_limit = node.config.get("incoming_queue_size_limit", 0)
237
+ node.incoming_queue_timeout = node.config.get("incoming_queue_timeout", 0)
96
238
  node.incoming_populate_thread = threading.Thread(
97
239
  target=populate_incoming_messages,
98
240
  args=(node,),
99
241
  daemon=True,
100
242
  )
101
- node.incoming_process_thread = threading.Thread(
102
- target=process_incoming_messages,
103
- args=(node,),
104
- daemon=True,
105
- )
106
- node.incoming_populate_thread.start()
107
- node.incoming_process_thread.start()
108
-
109
- node.outgoing_socket = socket.socket(
110
- socket.AF_INET6 if node.use_ipv6 else socket.AF_INET,
111
- socket.SOCK_DGRAM,
112
- )
243
+ node.incoming_process_thread = threading.Thread(
244
+ target=process_incoming_messages,
245
+ args=(node,),
246
+ daemon=True,
247
+ )
248
+ node.incoming_populate_thread.start()
249
+ node.incoming_process_thread.start()
250
+
251
+ node.outgoing_socket = socket.socket(
252
+ socket.AF_INET6 if node.use_ipv6 else socket.AF_INET,
253
+ socket.SOCK_DGRAM,
254
+ )
255
+ node.outgoing_socket.settimeout(0.5)
113
256
  node.outgoing_queue = Queue()
257
+ node.outgoing_queue_size = 0
258
+ node.outgoing_queue_size_lock = threading.RLock()
259
+ node.outgoing_queue_size_limit = node.config.get("outgoing_queue_size_limit", 0)
260
+ node.outgoing_queue_timeout = node.config.get("outgoing_queue_timeout", 0)
114
261
 
115
262
  node.outgoing_thread = threading.Thread(
116
263
  target=process_outgoing_messages,
117
264
  args=(node,),
118
265
  daemon=True,
119
266
  )
120
- node.outgoing_thread.start()
121
-
122
- # other workers & maps
123
- # track atom requests we initiated; guarded by atom_requests_lock on the node
124
- node.peer_manager_thread = threading.Thread(
125
- target=node._relay_peer_manager,
126
- daemon=True
267
+ node.outgoing_thread.start()
268
+
269
+ # other workers & maps
270
+ # track atom requests we initiated; guarded by atom_requests_lock on the node
271
+ node.peer_manager_thread = threading.Thread(
272
+ target=manage_peer,
273
+ args=(node,),
274
+ daemon=True
275
+ )
276
+ node.peer_manager_thread.start()
277
+
278
+ with node.peers_lock:
279
+ node.peers = {} # Dict[bytes,Peer]
280
+
281
+ latest_block_hex = config.get("latest_block_hash")
282
+ if latest_block_hex:
283
+ try:
284
+ node.latest_block_hash = hex_to_bytes(latest_block_hex, expected_length=32)
285
+ except Exception as exc:
286
+ node.logger.warning("Invalid latest_block_hash in config: %s", exc)
287
+ node.latest_block_hash = None
288
+ else:
289
+ node.latest_block_hash = None
290
+
291
+ node.logger.info(
292
+ "Communication ready (incoming_port=%s, outgoing_socket_initialized=%s, bootstrap_count=%s)",
293
+ node.config["incoming_port"],
294
+ node.outgoing_socket is not None,
295
+ len(node.bootstrap_peers),
127
296
  )
128
- node.peer_manager_thread.start()
129
-
130
- with node.peers_lock:
131
- node.peers, node.addresses = {}, {} # peers: Dict[bytes,Peer], addresses: Dict[(str,int),bytes]
132
-
133
- latest_block_hex = config.get("latest_block_hash")
134
- if latest_block_hex:
135
- try:
136
- node.latest_block_hash = hex_to_bytes(latest_block_hex, expected_length=32)
137
- except Exception as exc:
138
- node.logger.warning("Invalid latest_block_hash in config: %s", exc)
139
- node.latest_block_hash = None
140
- else:
141
- node.latest_block_hash = None
297
+ node.is_connected = True
142
298
 
143
- # bootstrap pings
144
- bootstrap_peers = config.get('bootstrap', [])
145
- for addr in bootstrap_peers:
299
+ # bootstrap pings (requires connected state for enqueue_outgoing)
300
+ for addr in node.bootstrap_peers:
146
301
  try:
147
302
  host, port = address_str_to_host_and_port(addr) # type: ignore[arg-type]
148
303
  except Exception as exc:
@@ -154,13 +309,19 @@ def communication_setup(node: "Node", config: dict):
154
309
  sender=node.relay_public_key,
155
310
  content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
156
311
  )
157
- node.outgoing_queue.put((handshake_message.to_bytes(), (host, port)))
312
+ enqueue_outgoing(
313
+ node,
314
+ (host, port),
315
+ message=handshake_message,
316
+ difficulty=1,
317
+ )
158
318
  node.logger.info("Sent bootstrap handshake to %s:%s", host, port)
159
-
160
- node.logger.info(
161
- "Communication ready (incoming_port=%s, outgoing_socket_initialized=%s, bootstrap_count=%s)",
162
- node.incoming_port,
163
- node.outgoing_socket is not None,
164
- len(bootstrap_peers),
319
+ if node.bootstrap_peers:
320
+ node._bootstrap_last_attempt = time.time()
321
+ advertise_cold_storage(node)
322
+ node.storage_index_thread = threading.Thread(
323
+ target=manage_storage_index,
324
+ args=(node,),
325
+ daemon=True,
165
326
  )
166
- node.is_connected = True
327
+ node.storage_index_thread.start()
@@ -1,5 +1,9 @@
1
1
  from typing import Tuple
2
2
 
3
+ from typing import TYPE_CHECKING
4
+ if TYPE_CHECKING:
5
+ from .. import Node
6
+
3
7
 
4
8
  def address_str_to_host_and_port(address: str) -> Tuple[str, int]:
5
9
  """Parse `host:port` (or `[ipv6]:port`) into a tuple."""
@@ -47,3 +51,13 @@ def xor_distance(a: bytes, b: bytes) -> int:
47
51
  if len(a) != len(b):
48
52
  raise ValueError("xor distance requires operands of equal length")
49
53
  return int.from_bytes(bytes(x ^ y for x, y in zip(a, b)), "big", signed=False)
54
+
55
+
56
+ def get_bootstrap_peers(node: "Node") -> list[str]:
57
+ default_seed = node.config["default_seed"]
58
+ additional_seeds = node.config["additional_seeds"]
59
+ peers = []
60
+ if default_seed is not None:
61
+ peers.append(default_seed)
62
+ peers.extend(additional_seeds)
63
+ return peers
astreum/node.py CHANGED
@@ -1,92 +1,102 @@
1
- """Core Astreum Node implementation."""
2
-
3
- from __future__ import annotations
4
-
5
- import threading
6
- import uuid
7
- from typing import Dict
8
-
9
- from astreum.communication.start import connect_to_network_and_verify
10
- from astreum.communication.models.peer import (
11
- add_peer as peers_add_peer,
12
- replace_peer as peers_replace_peer,
13
- get_peer as peers_get_peer,
14
- remove_peer as peers_remove_peer,
15
- )
16
- from astreum.consensus.start import process_blocks_and_transactions
17
- from astreum.machine import Expr, high_eval, low_eval, script_eval
18
- from astreum.machine.models.environment import Env, env_get, env_set
19
- from astreum.machine.models.expression import get_expr_list_from_storage
20
- from astreum.storage.models.atom import get_atom_list_from_storage
21
- from astreum.storage.actions.get import (
22
- _hot_storage_get,
23
- _cold_storage_get,
24
- _network_get,
25
- storage_get,
26
- local_get,
27
- )
28
- from astreum.storage.actions.set import (
29
- _hot_storage_set,
30
- _cold_storage_set,
31
- _network_set,
32
- )
33
- from astreum.storage.requests import add_atom_req, has_atom_req, pop_atom_req
34
- from astreum.storage.setup import storage_setup
35
- from astreum.utils.config import config_setup
36
- from astreum.utils.logging import logging_setup
37
-
38
-
1
+ """Core Astreum Node implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ import uuid
7
+ from typing import Dict
8
+
9
+ from astreum.communication.node import connect_node
10
+ from astreum.communication.util import get_bootstrap_peers
11
+ from astreum.communication.disconnect import disconnect_node
12
+ from astreum.communication.models.peer import (
13
+ add_peer as peers_add_peer,
14
+ replace_peer as peers_replace_peer,
15
+ get_peer as peers_get_peer,
16
+ remove_peer as peers_remove_peer,
17
+ )
18
+ from astreum.validation.node import validate_blockchain
19
+ from astreum.verification.node import verify_blockchain
20
+ from astreum.machine import Expr, high_eval, low_eval, script_eval
21
+ from astreum.machine.models.environment import Env, env_get, env_set
22
+ from astreum.machine.models.expression import get_expr_list_from_storage
23
+ from astreum.storage.models.atom import get_atom_list_from_storage
24
+ from astreum.storage.actions.get import (
25
+ _hot_storage_get,
26
+ _cold_storage_get,
27
+ _network_get,
28
+ storage_get,
29
+ local_get,
30
+ )
31
+ from astreum.storage.actions.set import (
32
+ _hot_storage_set,
33
+ _cold_storage_set,
34
+ _network_set,
35
+ )
36
+ from astreum.storage.requests import add_atom_req, has_atom_req, pop_atom_req
37
+ from astreum.storage.setup import storage_setup
38
+ from astreum.utils.config import config_setup
39
+ from astreum.utils.logging import logging_setup
40
+
41
+
39
42
  class Node:
40
43
  def __init__(self, config: dict = {}):
41
44
  self.config = config_setup(config=config)
42
-
43
- self.logger = logging_setup(self.config)
44
-
45
- self.logger.info("Starting Astreum Node")
46
-
47
- # Chain Configuration
48
- self.logger.info(f"Chain configured as: {self.config["chain"]} ({self.config["chain_id"]})")
49
-
50
- # Storage Setup
51
- storage_setup(self, config=self.config)
52
-
53
- # Machine Setup
54
- self.environments: Dict[uuid.UUID, Env] = {}
55
- self.machine_environments_lock = threading.RLock()
56
- self.is_connected = False
57
-
58
- connect = connect_to_network_and_verify
59
- validate = process_blocks_and_transactions
60
-
61
- low_eval = low_eval
62
- high_eval = high_eval
63
- script_eval = script_eval
64
-
65
- env_get = env_get
66
- env_set = env_set
67
-
68
- # Storage
69
- ## Get
70
- _hot_storage_get = _hot_storage_get
71
- _cold_storage_get = _cold_storage_get
72
- _network_get = _network_get
73
-
74
- ## Set
75
- _hot_storage_set = _hot_storage_set
76
- _cold_storage_set = _cold_storage_set
77
- _network_set = _network_set
78
-
79
- storage_get = storage_get
80
- local_get = local_get
81
-
82
- get_expr_list_from_storage = get_expr_list_from_storage
83
- get_atom_list_from_storage = get_atom_list_from_storage
84
-
85
- add_atom_req = add_atom_req
86
- has_atom_req = has_atom_req
87
- pop_atom_req = pop_atom_req
88
-
89
- add_peer = peers_add_peer
90
- replace_peer = peers_replace_peer
91
- get_peer = peers_get_peer
92
- remove_peer = peers_remove_peer
45
+ self.bootstrap_peers = get_bootstrap_peers(self)
46
+
47
+ self.logger = logging_setup(self.config)
48
+
49
+ self.logger.info("Starting Astreum Node")
50
+
51
+ # Chain Configuration
52
+ self.logger.info(f"Chain configured as: {self.config["chain"]} ({self.config["chain_id"]})")
53
+
54
+ # Storage Setup
55
+ storage_setup(self, config=self.config)
56
+
57
+ # Machine Setup
58
+ self.environments: Dict[uuid.UUID, Env] = {}
59
+ self.machine_environments_lock = threading.RLock()
60
+ self.is_connected = False
61
+ self.latest_block_hash = None
62
+ self.latest_block = None
63
+
64
+ connect = connect_node
65
+ disconnect = disconnect_node
66
+
67
+ verify = verify_blockchain
68
+
69
+ validate = validate_blockchain
70
+
71
+ low_eval = low_eval
72
+ high_eval = high_eval
73
+ script_eval = script_eval
74
+
75
+ env_get = env_get
76
+ env_set = env_set
77
+
78
+ # Storage
79
+ ## Get
80
+ _hot_storage_get = _hot_storage_get
81
+ _cold_storage_get = _cold_storage_get
82
+ _network_get = _network_get
83
+
84
+ ## Set
85
+ _hot_storage_set = _hot_storage_set
86
+ _cold_storage_set = _cold_storage_set
87
+ _network_set = _network_set
88
+
89
+ storage_get = storage_get
90
+ local_get = local_get
91
+
92
+ get_expr_list_from_storage = get_expr_list_from_storage
93
+ get_atom_list_from_storage = get_atom_list_from_storage
94
+
95
+ add_atom_req = add_atom_req
96
+ has_atom_req = has_atom_req
97
+ pop_atom_req = pop_atom_req
98
+
99
+ add_peer = peers_add_peer
100
+ replace_peer = peers_replace_peer
101
+ get_peer = peers_get_peer
102
+ remove_peer = peers_remove_peer