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/utils/config.py CHANGED
@@ -1,76 +1,293 @@
1
-
2
- from pathlib import Path
3
- from typing import Dict
4
-
1
+
2
+ from pathlib import Path
3
+ from typing import Dict
4
+
5
5
  DEFAULT_HOT_STORAGE_LIMIT = 1 << 30 # 1 GiB
6
6
  DEFAULT_COLD_STORAGE_LIMIT = 10 << 30 # 10 GiB
7
+ DEFAULT_COLD_STORAGE_ADVERTISE_LIMIT = 1000
8
+ DEFAULT_INCOMING_PORT = 52780
9
+ DEFAULT_LOGGING_RETENTION_DAYS = 90
7
10
  DEFAULT_PEER_TIMEOUT_SECONDS = 15 * 60 # 15 minutes
8
11
  DEFAULT_PEER_TIMEOUT_INTERVAL_SECONDS = 10 # 10 seconds
12
+ DEFAULT_BOOTSTRAP_RETRY_INTERVAL_SECONDS = 30 # 30 seconds
13
+ DEFAULT_STORAGE_INDEX_INTERVAL_SECONDS = 600 # 10 minutes
14
+ DEFAULT_ATOM_FETCH_INTERVAL_SECONDS = 0.25
15
+ DEFAULT_ATOM_FETCH_RETRIES = 8
16
+ DEFAULT_INCOMING_QUEUE_SIZE_LIMIT_BYTES = 64 * 1024 * 1024 # 64 MiB
17
+ DEFAULT_INCOMING_QUEUE_TIMEOUT_SECONDS = 1.0
18
+ DEFAULT_OUTGOING_QUEUE_SIZE_LIMIT_BYTES = 64 * 1024 * 1024 # 64 MiB
19
+ DEFAULT_OUTGOING_QUEUE_TIMEOUT_SECONDS = 1.0
20
+ DEFAULT_SEED = "bootstrap.astreum.org:52780"
21
+ DEFAULT_VERIFICATION_MAX_STALE_SECONDS = 10
22
+ DEFAULT_VERIFICATION_MAX_FUTURE_SKEW_SECONDS = 2
23
+
24
+
25
+ def config_setup(config: Dict = {}):
26
+ """
27
+ Normalize configuration values before the node starts.
28
+ """
29
+ chain_str = config.get("chain")
30
+ if chain_str not in {"main", "test"}:
31
+ chain_str = None
32
+ chain_id_raw = config.get("chain_id")
33
+ if chain_id_raw is None:
34
+ chain_id = 1 if chain_str == "main" else 0
35
+ else:
36
+ try:
37
+ chain_id = int(chain_id_raw)
38
+ except (TypeError, ValueError) as exc:
39
+ raise ValueError(
40
+ f"chain_id must be an integer: {chain_id_raw!r}"
41
+ ) from exc
42
+ if chain_str is None:
43
+ chain_str = "main" if chain_id == 1 else "test"
44
+ config["chain"] = chain_str
45
+ config["chain_id"] = chain_id
46
+
47
+ hot_limit_raw = config.get(
48
+ "hot_storage_limit", config.get("hot_storage_default_limit", DEFAULT_HOT_STORAGE_LIMIT)
49
+ )
50
+ try:
51
+ config["hot_storage_limit"] = int(hot_limit_raw)
52
+ except (TypeError, ValueError) as exc:
53
+ raise ValueError(
54
+ f"hot_storage_limit must be an integer: {hot_limit_raw!r}"
55
+ ) from exc
56
+
57
+ cold_limit_raw = config.get("cold_storage_limit", DEFAULT_COLD_STORAGE_LIMIT)
58
+ try:
59
+ config["cold_storage_limit"] = int(cold_limit_raw)
60
+ except (TypeError, ValueError) as exc:
61
+ raise ValueError(
62
+ f"cold_storage_limit must be an integer: {cold_limit_raw!r}"
63
+ ) from exc
64
+
65
+ cold_path_raw = config.get("cold_storage_path")
66
+ if cold_path_raw:
67
+ try:
68
+ path_obj = Path(cold_path_raw)
69
+ path_obj.mkdir(parents=True, exist_ok=True)
70
+ config["cold_storage_path"] = str(path_obj)
71
+ except OSError:
72
+ config["cold_storage_path"] = None
73
+ else:
74
+ config["cold_storage_path"] = None
9
75
 
10
-
11
- def config_setup(config: Dict = {}):
12
- """
13
- Normalize configuration values before the node starts.
14
- """
15
- chain_str = config.get("chain", "test")
16
- if chain_str not in {"main", "test"}:
17
- chain_str = "test"
18
- config["chain"] = chain_str
19
- config["chain_id"] = 1 if chain_str == "main" else 0
20
-
21
- hot_limit_raw = config.get(
22
- "hot_storage_limit", config.get("hot_storage_default_limit", DEFAULT_HOT_STORAGE_LIMIT)
76
+ advertise_limit_raw = config.get(
77
+ "cold_storage_advertise_limit", DEFAULT_COLD_STORAGE_ADVERTISE_LIMIT
23
78
  )
24
79
  try:
25
- config["hot_storage_default_limit"] = int(hot_limit_raw)
80
+ advertise_limit = int(advertise_limit_raw)
81
+ except (TypeError, ValueError) as exc:
82
+ raise ValueError(
83
+ "cold_storage_advertise_limit must be an integer: "
84
+ f"{advertise_limit_raw!r}"
85
+ ) from exc
86
+ if advertise_limit < -1:
87
+ raise ValueError(
88
+ "cold_storage_advertise_limit must be -1, 0, or a positive integer"
89
+ )
90
+ config["cold_storage_advertise_limit"] = advertise_limit
91
+
92
+ retention_raw = config.get(
93
+ "logging_retention_days",
94
+ config.get("logging_retention", config.get("retention_days", DEFAULT_LOGGING_RETENTION_DAYS)),
95
+ )
96
+ try:
97
+ config["logging_retention_days"] = int(retention_raw)
98
+ except (TypeError, ValueError) as exc:
99
+ raise ValueError(
100
+ f"logging_retention_days must be an integer: {retention_raw!r}"
101
+ ) from exc
102
+
103
+ if "incoming_port" in config:
104
+ incoming_port_raw = config["incoming_port"]
105
+ else:
106
+ incoming_port_raw = DEFAULT_INCOMING_PORT
107
+ try:
108
+ config["incoming_port"] = int(incoming_port_raw)
26
109
  except (TypeError, ValueError) as exc:
27
110
  raise ValueError(
28
- f"hot_storage_limit must be an integer: {hot_limit_raw!r}"
111
+ f"incoming_port must be an integer: {incoming_port_raw!r}"
29
112
  ) from exc
113
+ if config["incoming_port"] < 0:
114
+ raise ValueError("incoming_port must be 0 or a positive integer")
30
115
 
31
- cold_limit_raw = config.get("cold_storage_limit", DEFAULT_COLD_STORAGE_LIMIT)
116
+ incoming_queue_limit_raw = config.get(
117
+ "incoming_queue_size_limit", DEFAULT_INCOMING_QUEUE_SIZE_LIMIT_BYTES
118
+ )
32
119
  try:
33
- config["cold_storage_limit"] = int(cold_limit_raw)
120
+ incoming_queue_limit = int(incoming_queue_limit_raw)
34
121
  except (TypeError, ValueError) as exc:
35
122
  raise ValueError(
36
- f"cold_storage_limit must be an integer: {cold_limit_raw!r}"
123
+ f"incoming_queue_size_limit must be an integer: {incoming_queue_limit_raw!r}"
37
124
  ) from exc
125
+ if incoming_queue_limit < 0:
126
+ raise ValueError("incoming_queue_size_limit must be a non-negative integer")
127
+ config["incoming_queue_size_limit"] = incoming_queue_limit
38
128
 
39
- cold_path_raw = config.get("cold_storage_path")
40
- if cold_path_raw:
41
- try:
42
- path_obj = Path(cold_path_raw)
43
- path_obj.mkdir(parents=True, exist_ok=True)
44
- config["cold_storage_path"] = str(path_obj)
45
- except OSError:
46
- config["cold_storage_path"] = None
47
- else:
48
- config["cold_storage_path"] = None
129
+ incoming_queue_timeout_raw = config.get(
130
+ "incoming_queue_timeout", DEFAULT_INCOMING_QUEUE_TIMEOUT_SECONDS
131
+ )
132
+ try:
133
+ incoming_queue_timeout = float(incoming_queue_timeout_raw)
134
+ except (TypeError, ValueError) as exc:
135
+ raise ValueError(
136
+ f"incoming_queue_timeout must be a number: {incoming_queue_timeout_raw!r}"
137
+ ) from exc
138
+ if incoming_queue_timeout < 0:
139
+ raise ValueError("incoming_queue_timeout must be a non-negative number")
140
+ config["incoming_queue_timeout"] = incoming_queue_timeout
49
141
 
50
142
  peer_timeout_raw = config.get("peer_timeout", DEFAULT_PEER_TIMEOUT_SECONDS)
51
143
  try:
52
144
  peer_timeout = int(peer_timeout_raw)
53
145
  except (TypeError, ValueError) as exc:
54
146
  raise ValueError(
55
- f"peer_timeout must be an integer: {peer_timeout_raw!r}"
147
+ f"peer_timeout must be an integer: {peer_timeout_raw!r}"
148
+ ) from exc
149
+
150
+ if peer_timeout <= 0:
151
+ raise ValueError("peer_timeout must be a positive integer")
152
+
153
+ config["peer_timeout"] = peer_timeout
154
+
155
+ interval_raw = config.get("peer_timeout_interval", DEFAULT_PEER_TIMEOUT_INTERVAL_SECONDS)
156
+ try:
157
+ interval = int(interval_raw)
158
+ except (TypeError, ValueError) as exc:
159
+ raise ValueError(
160
+ f"peer_timeout_interval must be an integer: {interval_raw!r}"
161
+ ) from exc
162
+
163
+ if interval <= 0:
164
+ raise ValueError("peer_timeout_interval must be a positive integer")
165
+
166
+ config["peer_timeout_interval"] = interval
167
+
168
+ bootstrap_retry_raw = config.get(
169
+ "bootstrap_retry_interval", DEFAULT_BOOTSTRAP_RETRY_INTERVAL_SECONDS
170
+ )
171
+ try:
172
+ bootstrap_retry_interval = int(bootstrap_retry_raw)
173
+ except (TypeError, ValueError) as exc:
174
+ raise ValueError(
175
+ f"bootstrap_retry_interval must be an integer: {bootstrap_retry_raw!r}"
176
+ ) from exc
177
+ if bootstrap_retry_interval <= 0:
178
+ raise ValueError("bootstrap_retry_interval must be a positive integer")
179
+ config["bootstrap_retry_interval"] = bootstrap_retry_interval
180
+
181
+ storage_index_raw = config.get(
182
+ "storage_index_interval", DEFAULT_STORAGE_INDEX_INTERVAL_SECONDS
183
+ )
184
+ try:
185
+ storage_index_interval = int(storage_index_raw)
186
+ except (TypeError, ValueError) as exc:
187
+ raise ValueError(
188
+ f"storage_index_interval must be an integer: {storage_index_raw!r}"
56
189
  ) from exc
190
+ if storage_index_interval <= 0:
191
+ raise ValueError("storage_index_interval must be a positive integer")
192
+ config["storage_index_interval"] = storage_index_interval
57
193
 
58
- if peer_timeout <= 0:
59
- raise ValueError("peer_timeout must be a positive integer")
194
+ atom_fetch_interval_raw = config.get(
195
+ "atom_fetch_interval", DEFAULT_ATOM_FETCH_INTERVAL_SECONDS
196
+ )
197
+ try:
198
+ atom_fetch_interval = float(atom_fetch_interval_raw)
199
+ except (TypeError, ValueError) as exc:
200
+ raise ValueError(
201
+ f"atom_fetch_interval must be a number: {atom_fetch_interval_raw!r}"
202
+ ) from exc
203
+ if atom_fetch_interval < 0:
204
+ raise ValueError("atom_fetch_interval must be a non-negative number")
205
+ config["atom_fetch_interval"] = atom_fetch_interval
60
206
 
61
- config["peer_timeout"] = peer_timeout
207
+ atom_fetch_retries_raw = config.get(
208
+ "atom_fetch_retries", DEFAULT_ATOM_FETCH_RETRIES
209
+ )
210
+ try:
211
+ atom_fetch_retries = int(atom_fetch_retries_raw)
212
+ except (TypeError, ValueError) as exc:
213
+ raise ValueError(
214
+ f"atom_fetch_retries must be an integer: {atom_fetch_retries_raw!r}"
215
+ ) from exc
216
+ if atom_fetch_retries < 0:
217
+ raise ValueError("atom_fetch_retries must be a non-negative integer")
218
+ config["atom_fetch_retries"] = atom_fetch_retries
62
219
 
63
- interval_raw = config.get("peer_timeout_interval", DEFAULT_PEER_TIMEOUT_INTERVAL_SECONDS)
220
+ outgoing_queue_limit_raw = config.get(
221
+ "outgoing_queue_size_limit", DEFAULT_OUTGOING_QUEUE_SIZE_LIMIT_BYTES
222
+ )
64
223
  try:
65
- interval = int(interval_raw)
224
+ outgoing_queue_limit = int(outgoing_queue_limit_raw)
66
225
  except (TypeError, ValueError) as exc:
67
226
  raise ValueError(
68
- f"peer_timeout_interval must be an integer: {interval_raw!r}"
227
+ f"outgoing_queue_size_limit must be an integer: {outgoing_queue_limit_raw!r}"
69
228
  ) from exc
229
+ if outgoing_queue_limit < 0:
230
+ raise ValueError("outgoing_queue_size_limit must be a non-negative integer")
231
+ config["outgoing_queue_size_limit"] = outgoing_queue_limit
70
232
 
71
- if interval <= 0:
72
- raise ValueError("peer_timeout_interval must be a positive integer")
233
+ outgoing_queue_timeout_raw = config.get(
234
+ "outgoing_queue_timeout", DEFAULT_OUTGOING_QUEUE_TIMEOUT_SECONDS
235
+ )
236
+ try:
237
+ outgoing_queue_timeout = float(outgoing_queue_timeout_raw)
238
+ except (TypeError, ValueError) as exc:
239
+ raise ValueError(
240
+ f"outgoing_queue_timeout must be a number: {outgoing_queue_timeout_raw!r}"
241
+ ) from exc
242
+ if outgoing_queue_timeout < 0:
243
+ raise ValueError("outgoing_queue_timeout must be a non-negative number")
244
+ config["outgoing_queue_timeout"] = outgoing_queue_timeout
73
245
 
74
- config["peer_timeout_interval"] = interval
246
+ max_stale_raw = config.get(
247
+ "verification_max_stale_seconds", DEFAULT_VERIFICATION_MAX_STALE_SECONDS
248
+ )
249
+ try:
250
+ max_stale = int(max_stale_raw)
251
+ except (TypeError, ValueError) as exc:
252
+ raise ValueError(
253
+ f"verification_max_stale_seconds must be an integer: {max_stale_raw!r}"
254
+ ) from exc
255
+ if max_stale < 0:
256
+ raise ValueError("verification_max_stale_seconds must be a non-negative integer")
257
+ config["verification_max_stale_seconds"] = max_stale
75
258
 
76
- return config
259
+ max_future_raw = config.get(
260
+ "verification_max_future_skew", DEFAULT_VERIFICATION_MAX_FUTURE_SKEW_SECONDS
261
+ )
262
+ try:
263
+ max_future = int(max_future_raw)
264
+ except (TypeError, ValueError) as exc:
265
+ raise ValueError(
266
+ f"verification_max_future_skew must be an integer: {max_future_raw!r}"
267
+ ) from exc
268
+ if max_future < 0:
269
+ raise ValueError("verification_max_future_skew must be a non-negative integer")
270
+ config["verification_max_future_skew"] = max_future
271
+
272
+ if "default_seeds" in config:
273
+ raise ValueError("default_seeds is no longer supported; use default_seed")
274
+
275
+ if "default_seed" in config:
276
+ default_seed_raw = config["default_seed"]
277
+ else:
278
+ default_seed_raw = DEFAULT_SEED
279
+
280
+ if default_seed_raw is None:
281
+ config["default_seed"] = None
282
+ elif isinstance(default_seed_raw, str):
283
+ config["default_seed"] = default_seed_raw
284
+ else:
285
+ raise ValueError("default_seed must be a string or None")
286
+
287
+ additional_seeds_raw = config.get("additional_seeds", [])
288
+ if isinstance(additional_seeds_raw, (list, tuple)):
289
+ config["additional_seeds"] = list(additional_seeds_raw)
290
+ else:
291
+ raise ValueError("additional_seeds must be a list of strings")
292
+
293
+ return config
astreum/utils/logging.py CHANGED
@@ -159,7 +159,7 @@ def logging_setup(config: dict) -> logging.LoggerAdapter:
159
159
  product = _PRODUCT_NAME
160
160
  instance_id = _derive_instance_id()
161
161
 
162
- retention_value = config.get("retention_days")
162
+ retention_value = config.get("logging_retention_days")
163
163
  retention_days = int(retention_value) if retention_value is not None else 90
164
164
 
165
165
  verbose = bool(config.get("verbose", False))
@@ -1,20 +1,16 @@
1
1
  from .models.account import Account
2
2
  from .models.accounts import Accounts
3
3
  from .models.block import Block
4
- from .models.chain import Chain
5
4
  from .models.fork import Fork
6
5
  from .models.receipt import Receipt
7
6
  from .models.transaction import Transaction
8
- from .setup import consensus_setup
9
7
 
10
8
 
11
9
  __all__ = [
12
10
  "Block",
13
- "Chain",
14
11
  "Fork",
15
12
  "Receipt",
16
13
  "Transaction",
17
14
  "Account",
18
15
  "Accounts",
19
- "consensus_setup",
20
16
  ]
@@ -0,0 +1,2 @@
1
+ TREASURY_ADDRESS = b"\x01" * 32
2
+ BURN_ADDRESS = b"\x00" * 32
@@ -2,16 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any, List
4
4
 
5
+ from .constants import BURN_ADDRESS, TREASURY_ADDRESS
5
6
  from .models.account import Account
6
7
  from .models.accounts import Accounts
7
8
  from .models.block import Block
8
9
  from ..storage.models.atom import ZERO32
9
10
  from ..storage.models.trie import Trie
10
11
  from ..utils.integer import int_to_bytes
11
-
12
- TREASURY_ADDRESS = b"\x01" * 32
13
- BURN_ADDRESS = b"\x00" * 32
14
-
12
+ from time import time
15
13
 
16
14
  def create_genesis_block(
17
15
  node: Any,
@@ -49,13 +47,13 @@ def create_genesis_block(
49
47
  previous_block_hash=ZERO32,
50
48
  previous_block=None,
51
49
  number=0,
52
- timestamp=0,
50
+ timestamp=int(time()),
53
51
  accounts_hash=accounts_root,
54
52
  transactions_total_fees=0,
55
53
  transactions_hash=ZERO32,
56
54
  receipts_hash=ZERO32,
57
55
  delay_difficulty=0,
58
- validator_public_key=validator_pk,
56
+ validator_public_key_bytes=validator_pk,
59
57
  nonce=0,
60
58
  signature=b"",
61
59
  accounts=accounts,
@@ -35,7 +35,7 @@ class Account:
35
35
  @classmethod
36
36
  def from_atom(cls, node: Any, root_id: bytes) -> "Account":
37
37
 
38
- account_atoms = node.get_atom_list_from_storage(root_hash=root_id)
38
+ account_atoms = node.get_atom_list(root_id)
39
39
 
40
40
  if account_atoms is None or len(account_atoms) != 5:
41
41
  raise ValueError("malformed account atom list")