astreum 0.2.61__py3-none-any.whl → 0.3.9__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 (86) hide show
  1. astreum/__init__.py +16 -7
  2. astreum/{_communication → communication}/__init__.py +3 -3
  3. astreum/communication/handlers/handshake.py +89 -0
  4. astreum/communication/handlers/object_request.py +176 -0
  5. astreum/communication/handlers/object_response.py +115 -0
  6. astreum/communication/handlers/ping.py +34 -0
  7. astreum/communication/handlers/route_request.py +76 -0
  8. astreum/communication/handlers/route_response.py +53 -0
  9. astreum/communication/models/__init__.py +0 -0
  10. astreum/communication/models/message.py +124 -0
  11. astreum/communication/models/peer.py +51 -0
  12. astreum/{_communication → communication/models}/route.py +7 -12
  13. astreum/communication/processors/__init__.py +0 -0
  14. astreum/communication/processors/incoming.py +98 -0
  15. astreum/communication/processors/outgoing.py +20 -0
  16. astreum/communication/setup.py +166 -0
  17. astreum/communication/start.py +37 -0
  18. astreum/{_communication → communication}/util.py +7 -0
  19. astreum/consensus/__init__.py +20 -0
  20. astreum/consensus/genesis.py +66 -0
  21. astreum/consensus/models/__init__.py +0 -0
  22. astreum/consensus/models/account.py +84 -0
  23. astreum/consensus/models/accounts.py +72 -0
  24. astreum/consensus/models/block.py +364 -0
  25. astreum/{_consensus → consensus/models}/chain.py +7 -7
  26. astreum/{_consensus → consensus/models}/fork.py +8 -8
  27. astreum/consensus/models/receipt.py +98 -0
  28. astreum/{_consensus → consensus/models}/transaction.py +76 -78
  29. astreum/{_consensus → consensus}/setup.py +18 -50
  30. astreum/consensus/start.py +67 -0
  31. astreum/consensus/validator.py +95 -0
  32. astreum/{_consensus → consensus}/workers/discovery.py +19 -1
  33. astreum/consensus/workers/validation.py +307 -0
  34. astreum/{_consensus → consensus}/workers/verify.py +29 -2
  35. astreum/crypto/chacha20poly1305.py +74 -0
  36. astreum/machine/__init__.py +20 -0
  37. astreum/machine/evaluations/__init__.py +0 -0
  38. astreum/{_lispeum → machine/evaluations}/high_evaluation.py +237 -236
  39. astreum/machine/evaluations/low_evaluation.py +281 -0
  40. astreum/machine/evaluations/script_evaluation.py +27 -0
  41. astreum/machine/models/__init__.py +0 -0
  42. astreum/machine/models/environment.py +31 -0
  43. astreum/{_lispeum → machine/models}/expression.py +36 -8
  44. astreum/machine/tokenizer.py +90 -0
  45. astreum/node.py +78 -767
  46. astreum/storage/__init__.py +7 -0
  47. astreum/storage/actions/get.py +183 -0
  48. astreum/storage/actions/set.py +178 -0
  49. astreum/{_storage → storage/models}/atom.py +55 -57
  50. astreum/{_storage/patricia.py → storage/models/trie.py} +227 -203
  51. astreum/storage/requests.py +28 -0
  52. astreum/storage/setup.py +22 -15
  53. astreum/utils/config.py +48 -0
  54. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/METADATA +27 -26
  55. astreum-0.3.9.dist-info/RECORD +71 -0
  56. astreum/_communication/message.py +0 -101
  57. astreum/_communication/peer.py +0 -23
  58. astreum/_communication/setup.py +0 -322
  59. astreum/_consensus/__init__.py +0 -20
  60. astreum/_consensus/account.py +0 -95
  61. astreum/_consensus/accounts.py +0 -38
  62. astreum/_consensus/block.py +0 -311
  63. astreum/_consensus/genesis.py +0 -72
  64. astreum/_consensus/receipt.py +0 -136
  65. astreum/_consensus/workers/validation.py +0 -125
  66. astreum/_lispeum/__init__.py +0 -16
  67. astreum/_lispeum/environment.py +0 -13
  68. astreum/_lispeum/low_evaluation.py +0 -123
  69. astreum/_lispeum/tokenizer.py +0 -22
  70. astreum/_node.py +0 -198
  71. astreum/_storage/__init__.py +0 -7
  72. astreum/_storage/setup.py +0 -35
  73. astreum/format.py +0 -75
  74. astreum/models/block.py +0 -441
  75. astreum/models/merkle.py +0 -205
  76. astreum/models/patricia.py +0 -393
  77. astreum/storage/object.py +0 -68
  78. astreum-0.2.61.dist-info/RECORD +0 -57
  79. /astreum/{models → communication/handlers}/__init__.py +0 -0
  80. /astreum/{_communication → communication/models}/ping.py +0 -0
  81. /astreum/{_consensus → consensus}/workers/__init__.py +0 -0
  82. /astreum/{_lispeum → machine/models}/meter.py +0 -0
  83. /astreum/{_lispeum → machine}/parser.py +0 -0
  84. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/WHEEL +0 -0
  85. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/licenses/LICENSE +0 -0
  86. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/top_level.txt +0 -0
@@ -1,311 +0,0 @@
1
-
2
- from typing import Any, Callable, List, Optional, Tuple, TYPE_CHECKING
3
-
4
- from .._storage.atom import Atom, AtomKind, ZERO32
5
-
6
- if TYPE_CHECKING:
7
- from .._storage.patricia import PatriciaTrie
8
- from .transaction import Transaction
9
- from .receipt import Receipt
10
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
11
- from cryptography.exceptions import InvalidSignature
12
-
13
-
14
- def _int_to_be_bytes(n: Optional[int]) -> bytes:
15
- if n is None:
16
- return b""
17
- n = int(n)
18
- if n == 0:
19
- return b"\x00"
20
- size = (n.bit_length() + 7) // 8
21
- return n.to_bytes(size, "big")
22
-
23
-
24
- def _be_bytes_to_int(b: Optional[bytes]) -> int:
25
- if not b:
26
- return 0
27
- return int.from_bytes(b, "big")
28
-
29
-
30
- class Block:
31
- """Validation Block representation using Atom storage.
32
-
33
- Top-level encoding:
34
- block_id = type_atom.object_id()
35
- chain: type_atom --next--> signature_atom --next--> body_list_atom --next--> ZERO32
36
- where: type_atom = Atom(kind=AtomKind.SYMBOL, data=b"block")
37
- signature_atom = Atom(kind=AtomKind.BYTES, data=<signature-bytes>)
38
- body_list_atom = Atom(kind=AtomKind.LIST, data=<body_head_id>)
39
-
40
- Details order in body_list:
41
- 0: previous_block_hash (bytes)
42
- 1: number (int -> big-endian bytes)
43
- 2: timestamp (int -> big-endian bytes)
44
- 3: accounts_hash (bytes)
45
- 4: transactions_total_fees (int -> big-endian bytes)
46
- 5: transactions_hash (bytes)
47
- 6: receipts_hash (bytes)
48
- 7: delay_difficulty (int -> big-endian bytes)
49
- 8: delay_output (bytes)
50
- 9: validator_public_key (bytes)
51
-
52
- Notes:
53
- - "body tree" is represented here by the body_list id (self.body_hash), not
54
- embedded again as a field to avoid circular references.
55
- - "signature" is a field on the class but is not required for validation
56
- navigation; include it in the instance but it is not encoded in atoms
57
- unless explicitly provided via details extension in the future.
58
- """
59
-
60
- # essential identifiers
61
- hash: bytes
62
- previous_block_hash: bytes
63
- previous_block: Optional["Block"]
64
-
65
- # block details
66
- number: Optional[int]
67
- timestamp: Optional[int]
68
- accounts_hash: Optional[bytes]
69
- transactions_total_fees: Optional[int]
70
- transactions_hash: Optional[bytes]
71
- receipts_hash: Optional[bytes]
72
- delay_difficulty: Optional[int]
73
- delay_output: Optional[bytes]
74
- validator_public_key: Optional[bytes]
75
-
76
- # additional
77
- body_hash: Optional[bytes]
78
- signature: Optional[bytes]
79
-
80
- # structures
81
- accounts: Optional["PatriciaTrie"]
82
- transactions: Optional[List["Transaction"]]
83
- receipts: Optional[List["Receipt"]]
84
-
85
-
86
-
87
- def __init__(self) -> None:
88
- # defaults for safety
89
- self.hash = b""
90
- self.previous_block_hash = ZERO32
91
- self.previous_block = None
92
- self.number = None
93
- self.timestamp = None
94
- self.accounts_hash = None
95
- self.transactions_total_fees = None
96
- self.transactions_hash = None
97
- self.receipts_hash = None
98
- self.delay_difficulty = None
99
- self.delay_output = None
100
- self.validator_public_key = None
101
- self.body_hash = None
102
- self.signature = None
103
- self.accounts = None
104
- self.transactions = None
105
- self.receipts = None
106
-
107
- def to_atom(self) -> Tuple[bytes, List[Atom]]:
108
- # Build body details as direct byte atoms, in defined order
109
- details_ids: List[bytes] = []
110
- block_atoms: List[Atom] = []
111
-
112
- def _emit(detail_bytes: bytes) -> None:
113
- atom = Atom.from_data(data=detail_bytes, kind=AtomKind.BYTES)
114
- details_ids.append(atom.object_id())
115
- block_atoms.append(atom)
116
-
117
- # 0: previous_block_hash
118
- _emit(self.previous_block_hash)
119
- # 1: number
120
- _emit(_int_to_be_bytes(self.number))
121
- # 2: timestamp
122
- _emit(_int_to_be_bytes(self.timestamp))
123
- # 3: accounts_hash
124
- _emit(self.accounts_hash or b"")
125
- # 4: transactions_total_fees
126
- _emit(_int_to_be_bytes(self.transactions_total_fees))
127
- # 5: transactions_hash
128
- _emit(self.transactions_hash or b"")
129
- # 6: receipts_hash
130
- _emit(self.receipts_hash or b"")
131
- # 7: delay_difficulty
132
- _emit(_int_to_be_bytes(self.delay_difficulty))
133
- # 8: delay_output
134
- _emit(self.delay_output or b"")
135
- # 9: validator_public_key
136
- _emit(self.validator_public_key or b"")
137
-
138
- # Build body list chain (head points to the first detail atom id)
139
- body_atoms: List[Atom] = []
140
- body_head = ZERO32
141
- for child_id in reversed(details_ids):
142
- node = Atom.from_data(data=child_id, next_hash=body_head, kind=AtomKind.BYTES)
143
- body_head = node.object_id()
144
- body_atoms.append(node)
145
- body_atoms.reverse()
146
-
147
- block_atoms.extend(body_atoms)
148
-
149
- body_list_atom = Atom.from_data(data=body_head, kind=AtomKind.LIST)
150
- self.body_hash = body_list_atom.object_id()
151
-
152
- # Signature atom links to body list atom; type atom links to signature atom
153
- sig_atom = Atom.from_data(data=self.signature, next_hash=self.body_hash, kind=AtomKind.BYTES)
154
- type_atom = Atom.from_data(data=b"block", next_hash=sig_atom.object_id(), kind=AtomKind.SYMBOL)
155
-
156
- block_atoms.append(body_list_atom)
157
- block_atoms.append(sig_atom)
158
- block_atoms.append(type_atom)
159
-
160
- self.hash = type_atom.object_id()
161
- return self.hash, block_atoms
162
-
163
- @classmethod
164
- def from_atom(cls, source: Any, block_id: bytes) -> "Block":
165
- storage_get: Optional[Callable[[bytes], Optional[Atom]]]
166
- if callable(source):
167
- storage_get = source
168
- else:
169
- storage_get = getattr(source, "storage_get", None)
170
- if not callable(storage_get):
171
- raise TypeError(
172
- "Block.from_atom requires a node with 'storage_get' or a callable storage getter"
173
- )
174
-
175
- def _atom_kind(atom: Optional[Atom]) -> Optional[AtomKind]:
176
- kind_value = getattr(atom, "kind", None)
177
- if isinstance(kind_value, AtomKind):
178
- return kind_value
179
- if isinstance(kind_value, int):
180
- try:
181
- return AtomKind(kind_value)
182
- except ValueError:
183
- return None
184
- return None
185
-
186
- def _require_atom(atom_id: Optional[bytes], context: str, expected_kind: Optional[AtomKind] = None) -> Atom:
187
- if not atom_id or atom_id == ZERO32:
188
- raise ValueError(f"missing {context}")
189
- atom = storage_get(atom_id)
190
- if atom is None:
191
- raise ValueError(f"missing {context}")
192
- if expected_kind is not None:
193
- kind = _atom_kind(atom)
194
- if kind is not expected_kind:
195
- raise ValueError(f"malformed {context}")
196
- return atom
197
-
198
- def _read_list(head_id: Optional[bytes], context: str) -> List[bytes]:
199
- entries: List[bytes] = []
200
- current = head_id
201
- if not current or current == ZERO32:
202
- return entries
203
- while current and current != ZERO32:
204
- node = storage_get(current)
205
- if node is None:
206
- raise ValueError(f"missing list node while decoding {context}")
207
- node_kind = _atom_kind(node)
208
- if node_kind is not AtomKind.BYTES:
209
- raise ValueError(f"list element must be bytes while decoding {context}")
210
- if len(node.data) != len(ZERO32):
211
- raise ValueError(f"list element payload has unexpected length while decoding {context}")
212
- entries.append(node.data)
213
- current = node.next
214
- return entries
215
-
216
- type_atom = _require_atom(block_id, "block type atom", AtomKind.SYMBOL)
217
- if type_atom.data != b"block":
218
- raise ValueError("not a block (type atom payload)")
219
-
220
- sig_atom = _require_atom(type_atom.next, "block signature atom", AtomKind.BYTES)
221
- body_list_id = sig_atom.next
222
- body_list_atom = _require_atom(body_list_id, "block body list atom", AtomKind.LIST)
223
- if body_list_atom.next and body_list_atom.next != ZERO32:
224
- raise ValueError("malformed block (body list tail)")
225
-
226
- body_child_ids = _read_list(body_list_atom.data, "block body")
227
-
228
- details: List[bytes] = []
229
- for idx, child_id in enumerate(body_child_ids):
230
- if idx >= 10:
231
- break
232
- if not child_id or child_id == ZERO32:
233
- details.append(b"")
234
- continue
235
- detail_atom = storage_get(child_id)
236
- details.append(detail_atom.data if detail_atom is not None else b"")
237
-
238
- if len(details) < 10:
239
- details.extend([b""] * (10 - len(details)))
240
-
241
- b = cls()
242
- b.hash = block_id
243
- b.body_hash = body_list_id
244
-
245
- get = lambda i: details[i] if i < len(details) else b""
246
- b.previous_block_hash = get(0) or ZERO32
247
- b.previous_block = None
248
- b.number = _be_bytes_to_int(get(1))
249
- b.timestamp = _be_bytes_to_int(get(2))
250
- b.accounts_hash = get(3) or None
251
- b.transactions_total_fees = _be_bytes_to_int(get(4))
252
- b.transactions_hash = get(5) or None
253
- b.receipts_hash = get(6) or None
254
- b.delay_difficulty = _be_bytes_to_int(get(7))
255
- b.delay_output = get(8) or None
256
- b.validator_public_key = get(9) or None
257
-
258
- b.signature = sig_atom.data if sig_atom is not None else None
259
-
260
- return b
261
-
262
- def validate(self, storage_get: Callable[[bytes], Optional[Atom]]) -> bool:
263
- """Validate this block against storage.
264
-
265
- Checks:
266
- - Signature: signature must verify over the body list id using the
267
- validator's public key.
268
- - Timestamp monotonicity: if previous block exists (not ZERO32), this
269
- block's timestamp must be >= previous.timestamp + 1.
270
- """
271
- # Unverifiable if critical fields are missing
272
- if not self.body_hash:
273
- return False
274
- if not self.signature:
275
- return False
276
- if not self.validator_public_key:
277
- return False
278
- if self.timestamp is None:
279
- return False
280
-
281
- # 1) Signature check over body hash
282
- try:
283
- pub = Ed25519PublicKey.from_public_bytes(bytes(self.validator_public_key))
284
- pub.verify(self.signature, self.body_hash)
285
- except InvalidSignature as e:
286
- raise ValueError("invalid signature") from e
287
-
288
- # 2) Timestamp monotonicity against previous block
289
- prev_ts: Optional[int] = None
290
- prev_hash = self.previous_block_hash or ZERO32
291
-
292
- if self.previous_block is not None:
293
- prev_ts = int(self.previous_block.timestamp or 0)
294
- prev_hash = self.previous_block.hash or prev_hash or ZERO32
295
-
296
- if prev_hash and prev_hash != ZERO32 and prev_ts is None:
297
- # If previous block cannot be loaded, treat as unverifiable, not malicious
298
- try:
299
- prev = Block.from_atom(storage_get, prev_hash)
300
- except Exception:
301
- return False
302
- prev_ts = int(prev.timestamp or 0)
303
-
304
- if prev_hash and prev_hash != ZERO32:
305
- if prev_ts is None:
306
- return False
307
- cur_ts = int(self.timestamp or 0)
308
- if cur_ts < prev_ts + 1:
309
- raise ValueError("timestamp must be at least prev+1")
310
-
311
- return True
@@ -1,72 +0,0 @@
1
-
2
- from __future__ import annotations
3
-
4
- from typing import Any, List
5
-
6
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
7
-
8
- from .account import Account
9
- from .block import Block
10
- from .._storage.atom import ZERO32
11
- from .._storage.patricia import PatriciaTrie
12
- from ..utils.integer import int_to_bytes
13
-
14
- TREASURY_ADDRESS = b"\x01" * 32
15
- BURN_ADDRESS = b"\x00" * 32
16
- def create_genesis_block(node: Any, validator_public_key: bytes, validator_secret_key: bytes) -> Block:
17
- validator_pk = bytes(validator_public_key)
18
-
19
- if len(validator_pk) != 32:
20
- raise ValueError("validator_public_key must be 32 bytes")
21
-
22
- # 1. Stake trie with single validator stake of 1 (encoded on 32 bytes).
23
- stake_trie = PatriciaTrie()
24
- stake_amount = int_to_bytes(1)
25
- stake_trie.put(storage_node=node, key=validator_pk, value=stake_amount)
26
- stake_root = stake_trie.root_hash
27
-
28
- # 2. Account trie with treasury, burn, and validator accounts.
29
- accounts_trie = PatriciaTrie()
30
-
31
- treasury_account = Account.create(balance=1, data=stake_root, counter=0)
32
- accounts_trie.put(storage_node=node, key=TREASURY_ADDRESS, value=treasury_account.hash)
33
-
34
- burn_account = Account.create(balance=0, data=b"", counter=0)
35
- accounts_trie.put(storage_node=node, key=BURN_ADDRESS, value=burn_account.hash)
36
-
37
- validator_account = Account.create(balance=0, data=b"", counter=0)
38
- accounts_trie.put(storage_node=node, key=validator_pk, value=validator_account.hash)
39
-
40
- accounts_root = accounts_trie.root_hash
41
- if accounts_root is None:
42
- raise ValueError("genesis accounts trie is empty")
43
-
44
- # 3. Assemble block metadata.
45
- block = Block()
46
- block.previous_block_hash = ZERO32
47
- block.number = 0
48
- block.timestamp = 0
49
- block.accounts_hash = accounts_root
50
- block.accounts = accounts_trie
51
- block.transactions_total_fees = 0
52
- block.transactions_hash = ZERO32
53
- block.receipts_hash = ZERO32
54
- block.delay_difficulty = 0
55
- block.delay_output = b""
56
- block.validator_public_key = validator_pk
57
- block.transactions = []
58
- block.receipts = []
59
-
60
- # 4. Sign the block body with the validator secret key.
61
- block.signature = b""
62
- block.to_atom()
63
-
64
- if block.body_hash is None:
65
- raise ValueError("failed to materialise genesis block body")
66
-
67
- secret = Ed25519PrivateKey.from_private_bytes(validator_secret_key)
68
- block.signature = secret.sign(block.body_hash)
69
- block_hash, _ = block.to_atom()
70
-
71
- block.hash = block_hash
72
- return block
@@ -1,136 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass, field
4
- from typing import Callable, List, Optional, Tuple
5
-
6
- from .._storage.atom import Atom, AtomKind, ZERO32
7
-
8
- STATUS_SUCCESS = 0
9
- STATUS_FAILED = 1
10
-
11
-
12
- def _int_to_be_bytes(value: Optional[int]) -> bytes:
13
- if value is None:
14
- return b""
15
- value = int(value)
16
- if value == 0:
17
- return b"\x00"
18
- size = (value.bit_length() + 7) // 8
19
- return value.to_bytes(size, "big")
20
-
21
-
22
- def _be_bytes_to_int(data: Optional[bytes]) -> int:
23
- if not data:
24
- return 0
25
- return int.from_bytes(data, "big")
26
-
27
-
28
- @dataclass
29
- class Receipt:
30
- transaction_hash: bytes = ZERO32
31
- cost: int = 0
32
- logs: bytes = b""
33
- status: int = 0
34
- hash: bytes = ZERO32
35
- atoms: List[Atom] = field(default_factory=list)
36
-
37
- def to_atom(self) -> Tuple[bytes, List[Atom]]:
38
- """Serialise the receipt into Atom storage."""
39
- if self.status not in (STATUS_SUCCESS, STATUS_FAILED):
40
- raise ValueError("unsupported receipt status")
41
-
42
- detail_specs = [
43
- (bytes(self.transaction_hash), AtomKind.LIST),
44
- (_int_to_be_bytes(self.status), AtomKind.BYTES),
45
- (_int_to_be_bytes(self.cost), AtomKind.BYTES),
46
- (bytes(self.logs), AtomKind.BYTES),
47
- ]
48
-
49
- detail_atoms: List[Atom] = []
50
- next_hash = ZERO32
51
- for payload, kind in reversed(detail_specs):
52
- atom = Atom.from_data(data=payload, next_hash=next_hash, kind=kind)
53
- detail_atoms.append(atom)
54
- next_hash = atom.object_id()
55
- detail_atoms.reverse()
56
-
57
- type_atom = Atom.from_data(
58
- data=b"receipt",
59
- next_hash=next_hash,
60
- kind=AtomKind.SYMBOL,
61
- )
62
-
63
- self.hash = type_atom.object_id()
64
- atoms = detail_atoms + [type_atom]
65
- return self.hash, atoms
66
-
67
- def atomize(self) -> Tuple[bytes, List[Atom]]:
68
- """Generate atoms for this receipt and cache them."""
69
- receipt_id, atoms = self.to_atom()
70
- self.hash = receipt_id
71
- self.atoms = atoms
72
- return receipt_id, atoms
73
-
74
- @classmethod
75
- def from_atom(
76
- cls,
77
- storage_get: Callable[[bytes], Optional[Atom]],
78
- receipt_id: bytes,
79
- ) -> Receipt:
80
- """Materialise a Receipt from Atom storage."""
81
- def _atom_kind(atom: Optional[Atom]) -> Optional[AtomKind]:
82
- kind_value = getattr(atom, "kind", None)
83
- if isinstance(kind_value, AtomKind):
84
- return kind_value
85
- if isinstance(kind_value, int):
86
- try:
87
- return AtomKind(kind_value)
88
- except ValueError:
89
- return None
90
- return None
91
-
92
- type_atom = storage_get(receipt_id)
93
- if type_atom is None:
94
- raise ValueError("missing receipt type atom")
95
- if _atom_kind(type_atom) is not AtomKind.SYMBOL:
96
- raise ValueError("malformed receipt (type kind)")
97
- if type_atom.data != b"receipt":
98
- raise ValueError("not a receipt (type payload)")
99
-
100
- details: List[Atom] = []
101
- current = type_atom.next
102
- while current and current != ZERO32 and len(details) < 4:
103
- atom = storage_get(current)
104
- if atom is None:
105
- raise ValueError("missing receipt detail atom")
106
- details.append(atom)
107
- current = atom.next
108
-
109
- if current and current != ZERO32:
110
- raise ValueError("too many receipt fields")
111
- if len(details) != 4:
112
- raise ValueError("incomplete receipt fields")
113
-
114
- tx_atom, status_atom, cost_atom, logs_atom = details
115
-
116
- if _atom_kind(tx_atom) is not AtomKind.LIST:
117
- raise ValueError("receipt transaction hash must be list-kind")
118
- if any(_atom_kind(atom) is not AtomKind.BYTES for atom in [status_atom, cost_atom, logs_atom]):
119
- raise ValueError("receipt detail atoms must be bytes-kind")
120
-
121
- transaction_hash_bytes = tx_atom.data or ZERO32
122
- status_bytes = status_atom.data
123
- cost_bytes = cost_atom.data
124
- logs_bytes = logs_atom.data
125
-
126
- status_value = _be_bytes_to_int(status_bytes)
127
- if status_value not in (STATUS_SUCCESS, STATUS_FAILED):
128
- raise ValueError("unsupported receipt status")
129
-
130
- return cls(
131
- transaction_hash=transaction_hash_bytes or ZERO32,
132
- cost=_be_bytes_to_int(cost_bytes),
133
- logs=logs_bytes,
134
- status=status_value,
135
- hash=bytes(receipt_id),
136
- )
@@ -1,125 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import time
4
- from queue import Empty
5
- from typing import Any, Callable
6
-
7
- from ..block import Block
8
- from ..transaction import apply_transaction
9
- from ..._storage.atom import bytes_list_to_atoms
10
- from ..._storage.patricia import PatriciaTrie
11
- from ..._communication.message import Message, MessageTopic
12
- from ..._communication.ping import Ping
13
-
14
-
15
- def make_validation_worker(
16
- node: Any,
17
- *,
18
- current_validator: Callable[[Any], bytes],
19
- ) -> Callable[[], None]:
20
- """Build the validation worker bound to the given node."""
21
-
22
- def _validation_worker() -> None:
23
- stop = node._validation_stop_event
24
- while not stop.is_set():
25
- validation_public_key = getattr(node, "validation_public_key", None)
26
- if not validation_public_key:
27
- time.sleep(0.5)
28
- continue
29
-
30
- scheduled_validator = current_validator(node)
31
-
32
- if scheduled_validator != validation_public_key:
33
- time.sleep(0.5)
34
- continue
35
-
36
- try:
37
- current_hash = node._validation_transaction_queue.get_nowait()
38
- except Empty:
39
- time.sleep(0.1)
40
- continue
41
-
42
- # create thread to perform vdf
43
-
44
- new_block = Block()
45
- new_block.validator_public_key = validation_public_key
46
- new_block.previous_block_hash = node.latest_block_hash
47
- try:
48
- new_block.previous_block = Block.from_atom(node, new_block.previous_block_hash)
49
- except Exception:
50
- continue
51
- new_block.accounts = PatriciaTrie(root_hash=new_block.previous_block.accounts_hash)
52
-
53
- # we may want to add a timer to process part of the txs only on a slow computer
54
- while True:
55
- try:
56
- apply_transaction(node, new_block, current_hash)
57
- except NotImplementedError:
58
- node._validation_transaction_queue.put(current_hash)
59
- time.sleep(0.5)
60
- break
61
- except Exception:
62
- pass
63
-
64
- try:
65
- current_hash = node._validation_transaction_queue.get_nowait()
66
- except Empty:
67
- break
68
-
69
- # create an atom list of transactions, save the list head hash as the block's transactions_hash
70
- transactions = new_block.transactions or []
71
- tx_hashes = [bytes(tx.hash) for tx in transactions if tx.hash]
72
- head_hash, _ = bytes_list_to_atoms(tx_hashes)
73
- new_block.transactions_hash = head_hash
74
-
75
- receipts = new_block.receipts or []
76
- receipt_hashes = [bytes(rcpt.hash) for rcpt in receipts if rcpt.hash]
77
- receipts_head, _ = bytes_list_to_atoms(receipt_hashes)
78
- new_block.receipts_hash = receipts_head
79
-
80
- # get vdf result, default to 0 for now
81
-
82
- # get timestamp or wait for a the next second from the previous block, rule is the next block must be atleast 1 second after the previous
83
- now = time.time()
84
- min_allowed = new_block.previous_block.timestamp + 1
85
- if now < min_allowed:
86
- time.sleep(max(0.0, min_allowed - now))
87
- now = time.time()
88
- new_block.timestamp = max(int(now), min_allowed)
89
-
90
- # atomize block
91
- new_block_hash, new_block_atoms = new_block.to_atom()
92
- # put as own latest block hash
93
- node.latest_block_hash = new_block_hash
94
-
95
- # ping peers in the validation route to update there records
96
- if node.validation_route and node.outgoing_queue and node.addresses:
97
- route_peers = {
98
- peer_key
99
- for bucket in getattr(node.validation_route, "buckets", {}).values()
100
- for peer_key in bucket
101
- }
102
- if route_peers:
103
- ping_payload = Ping(
104
- is_validator=True,
105
- latest_block=new_block_hash,
106
- ).to_bytes()
107
-
108
- message_bytes = Message(
109
- topic=MessageTopic.PING,
110
- content=ping_payload,
111
- ).to_bytes()
112
-
113
- for address, peer_key in node.addresses.items():
114
- if peer_key in route_peers:
115
- try:
116
- node.outgoing_queue.put((message_bytes, address))
117
- except Exception:
118
- pass
119
-
120
- # upload block atoms
121
-
122
- # upload receipt atoms
123
- # upload account atoms
124
-
125
- return _validation_worker
@@ -1,16 +0,0 @@
1
- from .expression import Expr
2
- from .environment import Env
3
- from .low_evaluation import low_eval
4
- from .meter import Meter
5
- from .parser import parse, ParseError
6
- from .tokenizer import tokenize
7
-
8
- __all__ = [
9
- "Env",
10
- "Expr",
11
- "low_eval",
12
- "Meter",
13
- "parse",
14
- "tokenize",
15
- "ParseError",
16
- ]
@@ -1,13 +0,0 @@
1
- from ast import Expr
2
- from typing import Dict, Optional
3
- import uuid
4
-
5
-
6
- class Env:
7
- def __init__(
8
- self,
9
- data: Optional[Dict[str, Expr]] = None,
10
- parent_id: Optional[uuid.UUID] = None,
11
- ):
12
- self.data: Dict[bytes, Expr] = {} if data is None else data
13
- self.parent_id = parent_id