astreum 0.2.41__py3-none-any.whl → 0.3.1__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 (82) hide show
  1. astreum/__init__.py +16 -7
  2. astreum/{_communication → communication}/__init__.py +3 -3
  3. astreum/communication/handlers/handshake.py +83 -0
  4. astreum/communication/handlers/ping.py +48 -0
  5. astreum/communication/handlers/storage_request.py +81 -0
  6. astreum/communication/models/__init__.py +0 -0
  7. astreum/{_communication → communication/models}/message.py +1 -0
  8. astreum/communication/models/peer.py +23 -0
  9. astreum/{_communication → communication/models}/route.py +45 -8
  10. astreum/{_communication → communication}/setup.py +46 -95
  11. astreum/communication/start.py +38 -0
  12. astreum/consensus/__init__.py +20 -0
  13. astreum/consensus/genesis.py +66 -0
  14. astreum/consensus/models/__init__.py +0 -0
  15. astreum/consensus/models/account.py +84 -0
  16. astreum/consensus/models/accounts.py +72 -0
  17. astreum/consensus/models/block.py +364 -0
  18. astreum/{_consensus → consensus/models}/chain.py +7 -7
  19. astreum/{_consensus → consensus/models}/fork.py +8 -8
  20. astreum/consensus/models/receipt.py +98 -0
  21. astreum/consensus/models/transaction.py +213 -0
  22. astreum/{_consensus → consensus}/setup.py +26 -11
  23. astreum/consensus/start.py +68 -0
  24. astreum/consensus/validator.py +95 -0
  25. astreum/{_consensus → consensus}/workers/discovery.py +20 -1
  26. astreum/consensus/workers/validation.py +291 -0
  27. astreum/{_consensus → consensus}/workers/verify.py +32 -3
  28. astreum/machine/__init__.py +20 -0
  29. astreum/machine/evaluations/__init__.py +0 -0
  30. astreum/machine/evaluations/high_evaluation.py +237 -0
  31. astreum/machine/evaluations/low_evaluation.py +281 -0
  32. astreum/machine/evaluations/script_evaluation.py +27 -0
  33. astreum/machine/models/__init__.py +0 -0
  34. astreum/machine/models/environment.py +31 -0
  35. astreum/machine/models/expression.py +218 -0
  36. astreum/{_lispeum → machine}/parser.py +26 -31
  37. astreum/machine/tokenizer.py +90 -0
  38. astreum/node.py +73 -781
  39. astreum/storage/__init__.py +7 -0
  40. astreum/storage/actions/get.py +69 -0
  41. astreum/storage/actions/set.py +132 -0
  42. astreum/storage/models/atom.py +107 -0
  43. astreum/{_storage/patricia.py → storage/models/trie.py} +236 -177
  44. astreum/storage/setup.py +44 -15
  45. astreum/utils/bytes.py +24 -0
  46. astreum/utils/integer.py +25 -0
  47. astreum/utils/logging.py +219 -0
  48. astreum-0.3.1.dist-info/METADATA +160 -0
  49. astreum-0.3.1.dist-info/RECORD +62 -0
  50. astreum/_communication/peer.py +0 -11
  51. astreum/_consensus/__init__.py +0 -20
  52. astreum/_consensus/account.py +0 -170
  53. astreum/_consensus/accounts.py +0 -67
  54. astreum/_consensus/block.py +0 -328
  55. astreum/_consensus/genesis.py +0 -141
  56. astreum/_consensus/receipt.py +0 -177
  57. astreum/_consensus/transaction.py +0 -192
  58. astreum/_consensus/workers/validation.py +0 -122
  59. astreum/_lispeum/__init__.py +0 -16
  60. astreum/_lispeum/environment.py +0 -13
  61. astreum/_lispeum/expression.py +0 -37
  62. astreum/_lispeum/high_evaluation.py +0 -177
  63. astreum/_lispeum/low_evaluation.py +0 -123
  64. astreum/_lispeum/tokenizer.py +0 -22
  65. astreum/_node.py +0 -58
  66. astreum/_storage/__init__.py +0 -5
  67. astreum/_storage/atom.py +0 -117
  68. astreum/format.py +0 -75
  69. astreum/models/block.py +0 -441
  70. astreum/models/merkle.py +0 -205
  71. astreum/models/patricia.py +0 -393
  72. astreum/storage/object.py +0 -68
  73. astreum-0.2.41.dist-info/METADATA +0 -146
  74. astreum-0.2.41.dist-info/RECORD +0 -53
  75. /astreum/{models → communication/handlers}/__init__.py +0 -0
  76. /astreum/{_communication → communication/models}/ping.py +0 -0
  77. /astreum/{_communication → communication}/util.py +0 -0
  78. /astreum/{_consensus → consensus}/workers/__init__.py +0 -0
  79. /astreum/{_lispeum → machine/models}/meter.py +0 -0
  80. {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/WHEEL +0 -0
  81. {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/licenses/LICENSE +0 -0
  82. {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/top_level.txt +0 -0
@@ -1,67 +0,0 @@
1
- from __future__ import annotations
2
- from __future__ import annotations
3
-
4
- from typing import Any, Dict, Iterable, Optional, Tuple
5
-
6
- from .._storage.atom import Atom
7
- from .._storage.patricia import PatriciaTrie
8
- from .account import Account
9
-
10
-
11
- class Accounts:
12
- def __init__(
13
- self,
14
- root_hash: Optional[bytes] = None,
15
- ) -> None:
16
- self._trie = PatriciaTrie(root_hash=root_hash)
17
- self._cache: Dict[bytes, Account] = {}
18
- self._staged: Dict[bytes, Account] = {}
19
- self._staged_hashes: Dict[bytes, bytes] = {}
20
- self._staged_atoms: Dict[bytes, Iterable[Atom]] = {}
21
- self._node: Optional[Any] = None
22
-
23
- @property
24
- def root_hash(self) -> Optional[bytes]:
25
- return self._trie.root_hash
26
-
27
- def _resolve_node(self, node: Optional[Any]) -> Any:
28
- if node is not None:
29
- if self._node is None:
30
- self._node = node
31
- return node
32
- if self._node is None:
33
- raise ValueError("Accounts requires a node reference for trie access")
34
- return self._node
35
-
36
- def get_account(self, address: bytes, *, node: Optional[Any] = None) -> Optional[Account]:
37
- if address in self._staged:
38
- return self._staged[address]
39
-
40
- cached = self._cache.get(address)
41
- if cached is not None:
42
- return cached
43
-
44
- storage_node = self._resolve_node(node)
45
- account_id: Optional[bytes] = self._trie.get(storage_node, address)
46
- if account_id is None:
47
- return None
48
-
49
- account = Account.from_atom(storage_node, account_id)
50
- self._cache[address] = account
51
- return account
52
-
53
- def set_account(self, address: bytes, account: Account) -> None:
54
- account_hash, atoms = account.to_atom()
55
- self._staged[address] = account
56
- self._staged_hashes[address] = account_hash
57
- self._staged_atoms[address] = tuple(atoms)
58
- self._cache[address] = account
59
-
60
- def staged_items(self) -> Iterable[Tuple[bytes, Account]]:
61
- return self._staged.items()
62
-
63
- def staged_hashes(self) -> Dict[bytes, bytes]:
64
- return dict(self._staged_hashes)
65
-
66
- def staged_atoms(self) -> Dict[bytes, Iterable[Atom]]:
67
- return {addr: tuple(atoms) for addr, atoms in self._staged_atoms.items()}
@@ -1,328 +0,0 @@
1
-
2
- from typing import Any, Callable, List, Optional, Tuple, TYPE_CHECKING
3
-
4
- from .._storage.atom import Atom, 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
- def _make_list(child_ids: List[bytes]) -> Tuple[bytes, List[Atom]]:
31
- """Create a typed 'list' atom for child object ids.
32
-
33
- Encodes elements as a linked chain of element-atoms with data=child_id and
34
- next pointing to the next element's object id. The list value atom contains
35
- the element count and points to the head of the element chain. The type atom
36
- identifies the structure as a list.
37
- """
38
- acc: List[Atom] = []
39
- next_hash = ZERO32
40
- elem_atoms: List[Atom] = []
41
- # Build element chain in reverse, then flip to maintain forward order
42
- for h in reversed(child_ids):
43
- a = Atom.from_data(data=h, next_hash=next_hash)
44
- next_hash = a.object_id()
45
- elem_atoms.append(a)
46
- elem_atoms.reverse()
47
- head = next_hash
48
- val = Atom.from_data(data=(len(child_ids)).to_bytes(8, "little"), next_hash=head)
49
- typ = Atom.from_data(data=b"list", next_hash=val.object_id())
50
- return typ.object_id(), acc + elem_atoms + [val, typ]
51
-
52
-
53
- class Block:
54
- """Validation Block representation using Atom storage.
55
-
56
- Top-level encoding:
57
- block_id = list([ type_atom, body_list, signature_atom ])
58
- where: type_atom = Atom(data=b"block", next=body_list_id)
59
- body_list = list([...details...])
60
- signature_atom = Atom(data=<signature-bytes>)
61
-
62
- Details order in body_list:
63
- 0: previous_block_hash (bytes)
64
- 1: number (int → big-endian bytes)
65
- 2: timestamp (int → big-endian bytes)
66
- 3: accounts_hash (bytes)
67
- 4: transactions_total_fees (int → big-endian bytes)
68
- 5: transactions_hash (bytes)
69
- 6: receipts_hash (bytes)
70
- 7: delay_difficulty (int → big-endian bytes)
71
- 8: delay_output (bytes)
72
- 9: validator_public_key (bytes)
73
-
74
- Notes:
75
- - "body tree" is represented here by the body_list id (self.body_hash), not
76
- embedded again as a field to avoid circular references.
77
- - "signature" is a field on the class but is not required for validation
78
- navigation; include it in the instance but it is not encoded in atoms
79
- unless explicitly provided via details extension in the future.
80
- """
81
-
82
- # essential identifiers
83
- hash: bytes
84
- previous_block_hash: bytes
85
- previous_block: Optional["Block"]
86
-
87
- # block details
88
- number: Optional[int]
89
- timestamp: Optional[int]
90
- accounts_hash: Optional[bytes]
91
- transactions_total_fees: Optional[int]
92
- transactions_hash: Optional[bytes]
93
- receipts_hash: Optional[bytes]
94
- delay_difficulty: Optional[int]
95
- delay_output: Optional[bytes]
96
- validator_public_key: Optional[bytes]
97
-
98
- # additional
99
- body_hash: Optional[bytes]
100
- signature: Optional[bytes]
101
-
102
- # structures
103
- accounts: Optional["PatriciaTrie"]
104
- transactions: Optional[List["Transaction"]]
105
- receipts: Optional[List["Receipt"]]
106
-
107
-
108
-
109
- def __init__(self) -> None:
110
- # defaults for safety
111
- self.hash = b""
112
- self.previous_block_hash = ZERO32
113
- self.previous_block = None
114
- self.number = None
115
- self.timestamp = None
116
- self.accounts_hash = None
117
- self.transactions_total_fees = None
118
- self.transactions_hash = None
119
- self.receipts_hash = None
120
- self.delay_difficulty = None
121
- self.delay_output = None
122
- self.validator_public_key = None
123
- self.body_hash = None
124
- self.signature = None
125
- self.accounts = None
126
- self.transactions = None
127
- self.receipts = None
128
-
129
- def to_atom(self) -> Tuple[bytes, List[Atom]]:
130
- # Build body details as direct byte atoms, in defined order
131
- details_ids: List[bytes] = []
132
- atoms_acc: List[Atom] = []
133
-
134
- def _emit(detail_bytes: bytes) -> None:
135
- atom = Atom.from_data(data=detail_bytes)
136
- details_ids.append(atom.object_id())
137
- atoms_acc.append(atom)
138
-
139
- # 0: previous_block_hash
140
- prev_hash = self.previous_block_hash or (self.previous_block.hash if self.previous_block else b"")
141
- prev_hash = prev_hash or ZERO32
142
- self.previous_block_hash = prev_hash
143
- _emit(prev_hash)
144
- # 1: number
145
- _emit(_int_to_be_bytes(self.number))
146
- # 2: timestamp
147
- _emit(_int_to_be_bytes(self.timestamp))
148
- # 3: accounts_hash
149
- _emit(self.accounts_hash or b"")
150
- # 4: transactions_total_fees
151
- _emit(_int_to_be_bytes(self.transactions_total_fees))
152
- # 5: transactions_hash
153
- _emit(self.transactions_hash or b"")
154
- # 6: receipts_hash
155
- _emit(self.receipts_hash or b"")
156
- # 7: delay_difficulty
157
- _emit(_int_to_be_bytes(self.delay_difficulty))
158
- # 8: delay_output
159
- _emit(self.delay_output or b"")
160
- # 9: validator_public_key
161
- _emit(self.validator_public_key or b"")
162
-
163
- # Build body list
164
- body_id, body_atoms = _make_list(details_ids)
165
- atoms_acc.extend(body_atoms)
166
- self.body_hash = body_id
167
-
168
- # Type atom points to body list
169
- type_atom = Atom.from_data(data=b"block", next_hash=body_id)
170
-
171
- # Signature atom (raw byte payload)
172
- sig_atom = Atom.from_data(data=self.signature or b"", next_hash=ZERO32)
173
-
174
- # Main block list: [type_atom, body_list, signature]
175
- main_id, main_atoms = _make_list([type_atom.object_id(), body_id, sig_atom.object_id()])
176
- atoms_acc.append(type_atom)
177
- atoms_acc.append(sig_atom)
178
- atoms_acc.extend(main_atoms)
179
-
180
- self.hash = main_id
181
- return self.hash, atoms_acc
182
-
183
- @classmethod
184
- def from_atom(cls, source: Any, block_id: bytes) -> "Block":
185
- storage_get: Optional[Callable[[bytes], Optional[Atom]]]
186
- if callable(source):
187
- storage_get = source
188
- else:
189
- storage_get = getattr(source, "_local_get", None)
190
- if not callable(storage_get):
191
- raise TypeError("Block.from_atom requires a node with '_local_get' or a callable storage getter")
192
- # 1) Expect main list
193
- main_typ = storage_get(block_id)
194
- if main_typ is None or main_typ.data != b"list":
195
- raise ValueError("not a block (main list missing)")
196
- main_val = storage_get(main_typ.next)
197
- if main_val is None:
198
- raise ValueError("malformed block list (missing value)")
199
- # length is little-endian u64 per storage format
200
- if len(main_val.data) < 1:
201
- raise ValueError("malformed block list (length)")
202
- head = main_val.next
203
-
204
- # read first 2 elements: [type_atom_id, body_list_id]
205
- first_elem = storage_get(head)
206
- if first_elem is None:
207
- raise ValueError("malformed block list (head element)")
208
- type_atom_id = first_elem.data
209
- second_elem = storage_get(first_elem.next)
210
- if second_elem is None:
211
- raise ValueError("malformed block list (second element)")
212
- body_list_id = second_elem.data
213
- # optional 3rd element: signature atom id
214
- third_elem = storage_get(second_elem.next) if second_elem.next else None
215
- sig_atom_id: Optional[bytes] = third_elem.data if third_elem is not None else None
216
-
217
- # 2) Validate type atom and linkage to body
218
- type_atom = storage_get(type_atom_id)
219
- if type_atom is None or type_atom.data != b"block" or type_atom.next != body_list_id:
220
- raise ValueError("not a block (type atom)")
221
-
222
- # 3) Parse body list of details
223
- body_typ = storage_get(body_list_id)
224
- if body_typ is None or body_typ.data != b"list":
225
- raise ValueError("malformed body (type)")
226
- body_val = storage_get(body_typ.next)
227
- if body_val is None:
228
- raise ValueError("malformed body (value)")
229
- cur_elem_id = body_val.next
230
-
231
- def _read_detail_bytes(elem_id: bytes) -> bytes:
232
- elem = storage_get(elem_id)
233
- if elem is None:
234
- return b""
235
- child_id = elem.data
236
- detail = storage_get(child_id)
237
- return detail.data if detail is not None else b""
238
-
239
- details: List[bytes] = []
240
- # We read up to 10 fields if present
241
- for _ in range(10):
242
- if not cur_elem_id:
243
- break
244
- b = _read_detail_bytes(cur_elem_id)
245
- details.append(b)
246
- nxt = storage_get(cur_elem_id)
247
- cur_elem_id = nxt.next if nxt is not None else b""
248
-
249
- b = cls()
250
- b.hash = block_id
251
- b.body_hash = body_list_id
252
-
253
- # Map details back per the defined order
254
- get = lambda i: details[i] if i < len(details) else b""
255
- b.previous_block_hash = get(0) or ZERO32
256
- b.previous_block = None
257
- b.number = _be_bytes_to_int(get(1))
258
- b.timestamp = _be_bytes_to_int(get(2))
259
- b.accounts_hash = get(3) or None
260
- b.transactions_total_fees = _be_bytes_to_int(get(4))
261
- b.transactions_hash = get(5) or None
262
- b.receipts_hash = get(6) or None
263
- b.delay_difficulty = _be_bytes_to_int(get(7))
264
- b.delay_output = get(8) or None
265
- b.validator_public_key = get(9) or None
266
-
267
- # 4) Parse signature if present (supports raw or typed 'bytes' atom)
268
- if sig_atom_id is not None:
269
- sa = storage_get(sig_atom_id)
270
- if sa is not None:
271
- if sa.data == b"bytes":
272
- sval = storage_get(sa.next)
273
- b.signature = sval.data if sval is not None else b""
274
- else:
275
- b.signature = sa.data
276
-
277
- return b
278
-
279
- def validate(self, storage_get: Callable[[bytes], Optional[Atom]]) -> bool:
280
- """Validate this block against storage.
281
-
282
- Checks:
283
- - Signature: signature must verify over the body list id using the
284
- validator's public key.
285
- - Timestamp monotonicity: if previous block exists (not ZERO32), this
286
- block's timestamp must be >= previous.timestamp + 1.
287
- """
288
- # Unverifiable if critical fields are missing
289
- if not self.body_hash:
290
- return False
291
- if not self.signature:
292
- return False
293
- if not self.validator_public_key:
294
- return False
295
- if self.timestamp is None:
296
- return False
297
-
298
- # 1) Signature check over body hash
299
- try:
300
- pub = Ed25519PublicKey.from_public_bytes(bytes(self.validator_public_key))
301
- pub.verify(self.signature, self.body_hash)
302
- except InvalidSignature as e:
303
- raise ValueError("invalid signature") from e
304
-
305
- # 2) Timestamp monotonicity against previous block
306
- prev_ts: Optional[int] = None
307
- prev_hash = self.previous_block_hash or ZERO32
308
-
309
- if self.previous_block is not None:
310
- prev_ts = int(self.previous_block.timestamp or 0)
311
- prev_hash = self.previous_block.hash or prev_hash or ZERO32
312
-
313
- if prev_hash and prev_hash != ZERO32 and prev_ts is None:
314
- # If previous block cannot be loaded, treat as unverifiable, not malicious
315
- try:
316
- prev = Block.from_atom(storage_get, prev_hash)
317
- except Exception:
318
- return False
319
- prev_ts = int(prev.timestamp or 0)
320
-
321
- if prev_hash and prev_hash != ZERO32:
322
- if prev_ts is None:
323
- return False
324
- cur_ts = int(self.timestamp or 0)
325
- if cur_ts < prev_ts + 1:
326
- raise ValueError("timestamp must be at least prev+1")
327
-
328
- return True
@@ -1,141 +0,0 @@
1
-
2
- from __future__ import annotations
3
-
4
- from typing import Any, Iterable, List, Optional, Tuple
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 Atom, ZERO32
11
- from .._storage.patricia import PatriciaTrie, PatriciaNode
12
-
13
- TREASURY_ADDRESS = b"\x01" * 32
14
- BURN_ADDRESS = b"\x00" * 32
15
-
16
-
17
- def _int_to_be_bytes(value: int) -> bytes:
18
- if value < 0:
19
- raise ValueError("integer fields in genesis must be non-negative")
20
- if value == 0:
21
- return b"\x00"
22
- length = (value.bit_length() + 7) // 8
23
- return value.to_bytes(length, "big")
24
-
25
-
26
- def _make_list(child_ids: List[bytes]) -> Tuple[bytes, List[Atom]]:
27
- next_hash = ZERO32
28
- chain: List[Atom] = []
29
- for child_id in reversed(child_ids):
30
- elem = Atom.from_data(data=child_id, next_hash=next_hash)
31
- next_hash = elem.object_id()
32
- chain.append(elem)
33
- chain.reverse()
34
-
35
- value_atom = Atom.from_data(
36
- data=len(child_ids).to_bytes(8, "little"),
37
- next_hash=next_hash,
38
- )
39
- type_atom = Atom.from_data(data=b"list", next_hash=value_atom.object_id())
40
- atoms = chain + [value_atom, type_atom]
41
- return type_atom.object_id(), atoms
42
-
43
-
44
- def _store_atoms(node: Any, atoms: Iterable[Atom]) -> None:
45
- setter = getattr(node, "_local_set", None)
46
- if not callable(setter):
47
- raise TypeError("node must expose '_local_set(object_id, atom)'")
48
- for atom in atoms:
49
- setter(atom.object_id(), atom)
50
-
51
-
52
- def _persist_trie(trie: PatriciaTrie, node: Any) -> None:
53
- for patricia_node in trie.nodes.values():
54
- _, atoms = patricia_node.to_atoms()
55
- _store_atoms(node, atoms)
56
-
57
-
58
- if not hasattr(PatriciaNode, "to_bytes"):
59
- def _patricia_node_to_bytes(self: PatriciaNode) -> bytes: # type: ignore[no-redef]
60
- fields = [
61
- bytes([self.key_len]) + self.key,
62
- self.child_0 or ZERO32,
63
- self.child_1 or ZERO32,
64
- self.value or b"",
65
- ]
66
- encoded: List[bytes] = []
67
- for field in fields:
68
- encoded.append(len(field).to_bytes(4, "big"))
69
- encoded.append(field)
70
- return b"".join(encoded)
71
-
72
- PatriciaNode.to_bytes = _patricia_node_to_bytes # type: ignore[attr-defined]
73
-
74
-
75
- def create_genesis_block(node: Any, validator_public_key: bytes, validator_secret_key: bytes) -> Block:
76
- validator_pk = bytes(validator_public_key)
77
-
78
- if len(validator_pk) != 32:
79
- raise ValueError("validator_public_key must be 32 bytes")
80
-
81
- # 1. Stake trie with single validator stake of 1 (encoded on 32 bytes).
82
- stake_trie = PatriciaTrie()
83
- stake_amount = (1).to_bytes(32, "big")
84
- stake_trie.put(node, validator_pk, stake_amount)
85
- _persist_trie(stake_trie, node)
86
- stake_root = stake_trie.root_hash or ZERO32
87
-
88
- # 2. Account trie with treasury, burn, and validator accounts.
89
- accounts_trie = PatriciaTrie()
90
-
91
- treasury_account = Account.create(balance=1, data=stake_root, nonce=0)
92
- treasury_account_id, treasury_atoms = treasury_account.to_atom()
93
- _store_atoms(node, treasury_atoms)
94
- accounts_trie.put(node, TREASURY_ADDRESS, treasury_account_id)
95
-
96
- burn_account = Account.create(balance=0, data=b"", nonce=0)
97
- burn_account_id, burn_atoms = burn_account.to_atom()
98
- _store_atoms(node, burn_atoms)
99
- accounts_trie.put(node, BURN_ADDRESS, burn_account_id)
100
-
101
- validator_account = Account.create(balance=0, data=b"", nonce=0)
102
- validator_account_id, validator_atoms = validator_account.to_atom()
103
- _store_atoms(node, validator_atoms)
104
- accounts_trie.put(node, validator_pk, validator_account_id)
105
-
106
- _persist_trie(accounts_trie, node)
107
-
108
- accounts_root = accounts_trie.root_hash
109
- if accounts_root is None:
110
- raise ValueError("genesis accounts trie is empty")
111
-
112
- # 3. Assemble block metadata.
113
- block = Block()
114
- block.previous_block_hash = ZERO32
115
- block.number = 0
116
- block.timestamp = 0
117
- block.accounts_hash = accounts_root
118
- block.accounts = accounts_trie
119
- block.transactions_total_fees = 0
120
- block.transactions_hash = ZERO32
121
- block.receipts_hash = ZERO32
122
- block.delay_difficulty = 0
123
- block.delay_output = b""
124
- block.validator_public_key = validator_pk
125
- block.transactions = []
126
- block.receipts = []
127
-
128
- # 4. Sign the block body with the validator secret key.
129
- block.signature = b""
130
- block.to_atom()
131
-
132
- if block.body_hash is None:
133
- raise ValueError("failed to materialise genesis block body")
134
-
135
- secret = Ed25519PrivateKey.from_private_bytes(validator_secret_key)
136
- block.signature = secret.sign(block.body_hash)
137
- block_hash, block_atoms = block.to_atom()
138
- _store_atoms(node, block_atoms)
139
-
140
- block.hash = block_hash
141
- return block