astreum 0.2.12__tar.gz → 0.2.14__tar.gz

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.

Potentially problematic release.


This version of astreum might be problematic. Click here for more details.

Files changed (31) hide show
  1. {astreum-0.2.12/src/astreum.egg-info → astreum-0.2.14}/PKG-INFO +2 -2
  2. {astreum-0.2.12 → astreum-0.2.14}/README.md +1 -1
  3. {astreum-0.2.12 → astreum-0.2.14}/pyproject.toml +1 -1
  4. astreum-0.2.14/src/astreum/models/block.py +98 -0
  5. astreum-0.2.14/src/astreum/models/merkle.py +233 -0
  6. astreum-0.2.14/src/astreum/models/transaction.py +83 -0
  7. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/node.py +2 -175
  8. {astreum-0.2.12 → astreum-0.2.14/src/astreum.egg-info}/PKG-INFO +2 -2
  9. {astreum-0.2.12 → astreum-0.2.14}/src/astreum.egg-info/SOURCES.txt +5 -2
  10. {astreum-0.2.12 → astreum-0.2.14}/LICENSE +0 -0
  11. {astreum-0.2.12 → astreum-0.2.14}/setup.cfg +0 -0
  12. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/__init__.py +0 -0
  13. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/_node/__init__.py +0 -0
  14. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/_node/storage/__init__.py +0 -0
  15. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/_node/storage/merkle.py +0 -0
  16. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/_node/storage/patricia.py +0 -0
  17. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/crypto/__init__.py +0 -0
  18. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/crypto/ed25519.py +0 -0
  19. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/crypto/quadratic_form.py +0 -0
  20. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/crypto/wesolowski.py +0 -0
  21. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/crypto/x25519.py +0 -0
  22. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/format.py +0 -0
  23. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/lispeum/__init__.py +0 -0
  24. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/lispeum/parser.py +0 -0
  25. {astreum-0.2.12 → astreum-0.2.14}/src/astreum/lispeum/tokenizer.py +0 -0
  26. {astreum-0.2.12/src/astreum/utils → astreum-0.2.14/src/astreum/models}/__init__.py +0 -0
  27. {astreum-0.2.12/src/astreum/utils → astreum-0.2.14/src/astreum/models}/patricia.py +0 -0
  28. {astreum-0.2.12 → astreum-0.2.14}/src/astreum.egg-info/dependency_links.txt +0 -0
  29. {astreum-0.2.12 → astreum-0.2.14}/src/astreum.egg-info/requires.txt +0 -0
  30. {astreum-0.2.12 → astreum-0.2.14}/src/astreum.egg-info/top_level.txt +0 -0
  31. {astreum-0.2.12 → astreum-0.2.14}/tests/test_node_machine.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
5
5
  Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
6
6
  Project-URL: Homepage, https://github.com/astreum/lib
@@ -118,6 +118,6 @@ except ParseError as e:
118
118
  ## Testing
119
119
 
120
120
  ```bash
121
-
121
+ source venv/bin/activate
122
122
  python3 -m unittest discover -s tests
123
123
  ```
@@ -100,6 +100,6 @@ except ParseError as e:
100
100
  ## Testing
101
101
 
102
102
  ```bash
103
-
103
+ source venv/bin/activate
104
104
  python3 -m unittest discover -s tests
105
105
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astreum"
3
- version = "0.2.12"
3
+ version = "0.2.14"
4
4
  authors = [
5
5
  { name="Roy R. O. Okello", email="roy@stelar.xyz" },
6
6
  ]
@@ -0,0 +1,98 @@
1
+ from ..format import encode, decode
2
+ from ..crypto import ed25519
3
+ import blake3
4
+
5
+ class Block:
6
+ def __init__(
7
+ self,
8
+ number: int,
9
+ prev_block_hash: bytes,
10
+ timestamp: int,
11
+ accounts_hash: bytes,
12
+ transactions_total_fees: int,
13
+ transaction_limit: int,
14
+ transactions_root_hash: bytes,
15
+ vdf_difficulty: int,
16
+ vdf_output: bytes,
17
+ vdf_proof: bytes,
18
+ validator_pk: bytes,
19
+ signature: bytes,
20
+ ) -> None:
21
+ self.accounts_hash = accounts_hash
22
+ self.number = int(number)
23
+ self.prev_block_hash = prev_block_hash
24
+ self.timestamp = int(timestamp)
25
+ self.transactions_total_fees = int(transactions_total_fees)
26
+ self.transaction_limit = int(transaction_limit)
27
+ self.transactions_root_hash = transactions_root_hash
28
+ self.validator_pk = validator_pk
29
+ self.vdf_difficulty = int(vdf_difficulty)
30
+ self.vdf_output = vdf_output
31
+ self.vdf_proof = vdf_proof
32
+ self.signature = signature
33
+ self.body_hash = self._compute_body_hash()
34
+
35
+ def _body_fields_without_sig(self) -> list:
36
+ return [
37
+ self.accounts_hash,
38
+ self.number,
39
+ self.prev_block_hash,
40
+ self.timestamp,
41
+ self.transactions_total_fees,
42
+ self.transaction_limit,
43
+ self.transactions_root_hash,
44
+ self.validator_pk,
45
+ self.vdf_difficulty,
46
+ self.vdf_output,
47
+ self.vdf_proof,
48
+ ]
49
+
50
+ def _compute_body_hash(self) -> bytes:
51
+ return blake3.blake3(encode(self._body_fields_without_sig())).digest()
52
+
53
+ def to_bytes(self) -> bytes:
54
+ return encode(self._body_fields_without_sig() + [self.signature])
55
+
56
+ @classmethod
57
+ def from_bytes(cls, blob: bytes) -> "Block":
58
+ (
59
+ accounts_hash,
60
+ number,
61
+ prev_block_hash,
62
+ timestamp,
63
+ transactions_total_fees,
64
+ transaction_limit,
65
+ transactions_root_hash,
66
+ validator_pk,
67
+ vdf_difficulty,
68
+ vdf_output,
69
+ vdf_proof,
70
+ signature
71
+ ) = decode(blob)
72
+ return cls(
73
+ number=int(number),
74
+ prev_block_hash=prev_block_hash,
75
+ timestamp=int(timestamp),
76
+ accounts_hash=accounts_hash,
77
+ transactions_total_fees=int(transactions_total_fees),
78
+ transaction_limit=int(transaction_limit),
79
+ transactions_root_hash=transactions_root_hash,
80
+ vdf_difficulty=int(vdf_difficulty),
81
+ vdf_output=vdf_output,
82
+ vdf_proof=vdf_proof,
83
+ validator_pk=validator_pk,
84
+ signature=signature,
85
+ )
86
+
87
+ @property
88
+ def hash(self) -> bytes:
89
+ return blake3.blake3(self.body_hash + self.signature).digest()
90
+
91
+ def verify_block_signature(self) -> bool:
92
+ try:
93
+ pub = ed25519.Ed25519PublicKey.from_public_bytes(self.validator_pk)
94
+ pub.verify(self.signature, self.body_hash)
95
+ return True
96
+ except Exception:
97
+ return False
98
+
@@ -0,0 +1,233 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Dict, List, Optional, Tuple
4
+
5
+ import blake3 # type: ignore
6
+ from ..format import encode, decode
7
+
8
+ class MerkleNode:
9
+ """A node in a binary Merkle tree.
10
+
11
+ *Leaf* : ``value`` is **not** ``None``.
12
+ *Interior*: ``value`` is ``None``.
13
+ """
14
+
15
+ __slots__ = ("left", "right", "value", "_hash")
16
+
17
+ def __init__(
18
+ self,
19
+ left: Optional[bytes],
20
+ right: Optional[bytes],
21
+ value: Optional[bytes],
22
+ ) -> None:
23
+ self.left = left
24
+ self.right = right
25
+ self.value = value
26
+ self._hash: Optional[bytes] = None
27
+
28
+ # ------------------------------------------------------------------
29
+ # serialisation helpers
30
+ # ------------------------------------------------------------------
31
+ def to_bytes(self) -> bytes:
32
+ return encode([self.left, self.right, self.value])
33
+
34
+ @classmethod
35
+ def from_bytes(cls, blob: bytes) -> "MerkleNode":
36
+ left, right, value = decode(blob)
37
+ return cls(left, right, value)
38
+
39
+ # ------------------------------------------------------------------
40
+ # content hash (blake3)
41
+ # ------------------------------------------------------------------
42
+ def _compute_hash(self) -> bytes:
43
+ if self.value is not None: # leaf
44
+ return blake3.blake3(self.value).digest()
45
+ left = self.left or b""
46
+ right = self.right or b""
47
+ return blake3.blake3(left + right).digest()
48
+
49
+ def hash(self) -> bytes:
50
+ if self._hash is None:
51
+ self._hash = self._compute_hash()
52
+ return self._hash
53
+
54
+
55
+ # ─────────────────────────────────────────────────────────────────────────────
56
+ # Merkle tree – fixed‑height, no dynamic growth
57
+ # ─────────────────────────────────────────────────────────────────────────────
58
+
59
+
60
+ class MerkleTree:
61
+ """Binary Merkle tree addressed by *leaf position* (0‑based).
62
+
63
+ * The number of levels is fixed once the tree is built (``_height``).
64
+ * ``put`` can **only update** existing leaves; it never adds capacity.
65
+ """
66
+
67
+ # ------------------------------------------------------------------
68
+ # construction helpers
69
+ # ------------------------------------------------------------------
70
+ def __init__(
71
+ self,
72
+ node_get: Callable[[bytes], Optional[bytes]],
73
+ root_hash: Optional[bytes] = None,
74
+ height: Optional[int] = None,
75
+ ) -> None:
76
+ self._node_get = node_get
77
+ self.nodes: Dict[bytes, MerkleNode] = {}
78
+ self.root_hash = root_hash
79
+ self._height: Optional[int] = height
80
+
81
+ @classmethod
82
+ def from_leaves(
83
+ cls,
84
+ leaves: List[bytes],
85
+ node_get: Callable[[bytes], Optional[bytes]] | None = None,
86
+ ) -> "MerkleTree":
87
+ """Build a complete tree from *leaves* (left‑to‑right order).
88
+
89
+ Missing right siblings in the top levels are allowed; they are encoded
90
+ as interior nodes with a single child.
91
+ """
92
+ if not leaves:
93
+ raise ValueError("must supply at least one leaf")
94
+
95
+ node_get = node_get or (lambda _h: None)
96
+ tree = cls(node_get=node_get)
97
+
98
+ # Step 1 – create leaf nodes list[bytes]
99
+ level_hashes: List[bytes] = []
100
+ for val in leaves:
101
+ leaf = MerkleNode(None, None, val)
102
+ h = leaf.hash()
103
+ tree.nodes[h] = leaf
104
+ level_hashes.append(h)
105
+
106
+ height = 1 # current level (leaves)
107
+
108
+ # Step 2 – build upper levels until single root remains
109
+ while len(level_hashes) > 1:
110
+ next_level: List[bytes] = []
111
+ it = iter(level_hashes)
112
+ for left_hash in it:
113
+ try:
114
+ right_hash = next(it)
115
+ except StopIteration:
116
+ right_hash = None
117
+ parent = MerkleNode(left_hash, right_hash, None)
118
+ ph = parent.hash()
119
+ tree.nodes[ph] = parent
120
+ next_level.append(ph)
121
+ level_hashes = next_level
122
+ height += 1
123
+
124
+ tree.root_hash = level_hashes[0]
125
+ tree._height = height
126
+ return tree
127
+
128
+ # ------------------------------------------------------------------
129
+ # internal helpers
130
+ # ------------------------------------------------------------------
131
+ def _fetch(self, h: bytes | None) -> Optional[MerkleNode]:
132
+ if h is None:
133
+ return None
134
+ node = self.nodes.get(h)
135
+ if node is None:
136
+ raw = self._node_get(h)
137
+ if raw is None:
138
+ return None
139
+ node = MerkleNode.from_bytes(raw)
140
+ self.nodes[h] = node
141
+ return node
142
+
143
+ def _invalidate(self, node: MerkleNode) -> None:
144
+ node._hash = None # type: ignore[attr-defined]
145
+
146
+ def _ensure_height(self) -> None:
147
+ if self._height is None:
148
+ # Recompute by traversing leftmost branch
149
+ h = 0
150
+ nh = self.root_hash
151
+ while nh is not None:
152
+ node = self._fetch(nh)
153
+ nh = node.left if node and node.value is None else None
154
+ h += 1
155
+ self._height = h or 1
156
+
157
+ def _capacity(self) -> int:
158
+ self._ensure_height()
159
+ assert self._height is not None
160
+ return 1 << (self._height - 1)
161
+
162
+ def _path_bits(self, index: int) -> List[int]:
163
+ self._ensure_height()
164
+ assert self._height is not None
165
+ bits = []
166
+ for shift in range(self._height - 2, -1, -1):
167
+ bits.append((index >> shift) & 1)
168
+ return bits
169
+
170
+ # ------------------------------------------------------------------
171
+ # get / put
172
+ # ------------------------------------------------------------------
173
+ def get(self, index: int) -> Optional[bytes]:
174
+ if index < 0 or self.root_hash is None or index >= self._capacity():
175
+ return None
176
+
177
+ node_hash = self.root_hash
178
+ for bit in self._path_bits(index):
179
+ node = self._fetch(node_hash)
180
+ if node is None:
181
+ return None
182
+ node_hash = node.right if bit else node.left
183
+ if node_hash is None:
184
+ return None
185
+ leaf = self._fetch(node_hash)
186
+ return leaf.value if leaf else None
187
+
188
+ def put(self, index: int, value: bytes) -> None:
189
+ """Overwrite *value* at existing leaf *index*.
190
+
191
+ Raises ``IndexError`` if *index* is outside current capacity **or** if
192
+ the path to that leaf is missing in the stored structure.
193
+ """
194
+ if index < 0:
195
+ raise IndexError("negative index")
196
+ if self.root_hash is None:
197
+ raise IndexError("tree is empty – build it first with from_leaves()")
198
+ if index >= self._capacity():
199
+ raise IndexError("index beyond tree capacity")
200
+
201
+ node_hash = self.root_hash
202
+ stack: List[Tuple[MerkleNode, bytes, bool]] = []
203
+ for bit in self._path_bits(index):
204
+ node = self._fetch(node_hash)
205
+ if node is None:
206
+ raise IndexError("missing node along path")
207
+ went_right = bool(bit)
208
+ child_hash = node.right if went_right else node.left
209
+ if child_hash is None:
210
+ raise IndexError("path leads into non‑existent branch")
211
+ stack.append((node, node.hash(), went_right))
212
+ node_hash = child_hash
213
+
214
+ leaf = self._fetch(node_hash)
215
+ if leaf is None or leaf.value is None:
216
+ raise IndexError("target leaf missing")
217
+ leaf.value = value
218
+ self._invalidate(leaf)
219
+ new_hash = leaf.hash()
220
+
221
+ # bubble updated hashes
222
+ for parent, old_hash, went_right in reversed(stack):
223
+ if went_right:
224
+ parent.right = new_hash
225
+ else:
226
+ parent.left = new_hash
227
+ self._invalidate(parent)
228
+ new_hash = parent.hash()
229
+ if new_hash != old_hash:
230
+ del self.nodes[old_hash]
231
+ self.nodes[new_hash] = parent
232
+ self.root_hash = new_hash
233
+
@@ -0,0 +1,83 @@
1
+ from ..format import encode, decode
2
+ from ..crypto import ed25519
3
+ import blake3
4
+
5
+ class Transaction:
6
+ def __init__(
7
+ self,
8
+ sender_pk: bytes,
9
+ recipient_pk: bytes,
10
+ amount: int,
11
+ fee: int,
12
+ nonce: int,
13
+ signature: bytes | None = None,
14
+ ) -> None:
15
+ self.sender_pk = sender_pk
16
+ self.recipient_pk = recipient_pk
17
+ self.amount = amount
18
+ self.fee = fee
19
+ self.nonce = nonce
20
+ self.signature = signature
21
+
22
+ if self.amount < 0 or self.fee < 0:
23
+ raise ValueError("amount and fee must be non-negative")
24
+
25
+ if self.fee % 2 != 0:
26
+ raise ValueError("fee must be divisible by two")
27
+
28
+ self.tx_body_hash: bytes = blake3.blake3(self._body_bytes()).digest()
29
+
30
+ if self.signature is not None:
31
+ self.tx_hash = blake3.blake3(self.tx_body_hash + self.signature).digest()
32
+ else:
33
+ self.tx_hash = None
34
+
35
+ def sign(self, priv_key: ed25519.Ed25519PrivateKey) -> None:
36
+ if self.signature is not None:
37
+ raise ValueError("transaction already signed")
38
+ sig = priv_key.sign(self.tx_body_hash)
39
+ self.signature = sig
40
+ self.tx_hash = blake3.blake3(self.tx_body_hash + sig).digest()
41
+
42
+ def verify_signature(self) -> bool:
43
+ if self.signature is None:
44
+ return False
45
+ try:
46
+ pub = ed25519.Ed25519PublicKey.from_public_bytes(self.sender_pk)
47
+ pub.verify(self.signature, self.tx_body_hash)
48
+ return True
49
+ except Exception:
50
+ return False
51
+
52
+ def to_bytes(self) -> bytes:
53
+ sig = self.signature or b""
54
+ return encode([
55
+ self.sender_pk,
56
+ self.recipient_pk,
57
+ self.amount,
58
+ self.fee,
59
+ self.nonce,
60
+ sig,
61
+ ])
62
+
63
+ @classmethod
64
+ def from_bytes(cls, blob: bytes) -> 'Transaction':
65
+ sender, recipient, amount, fee, nonce, sig = decode(blob)
66
+ return cls(sender, recipient, int(amount), int(fee), int(nonce), sig)
67
+
68
+ def _body_bytes(self) -> bytes:
69
+ return encode([
70
+ self.sender_pk,
71
+ self.recipient_pk,
72
+ self.amount,
73
+ self.fee,
74
+ self.nonce,
75
+ ])
76
+
77
+ def __eq__(self, other: "Transaction") -> bool:
78
+ if not isinstance(other, Transaction):
79
+ return NotImplemented
80
+ return self.tx_hash == other.tx_hash
81
+
82
+ def __hash__(self) -> int:
83
+ return int.from_bytes(self.tx_hash, 'big')
@@ -6,6 +6,8 @@ from pathlib import Path
6
6
  from typing import Tuple, Dict, Union, Optional, List
7
7
  from datetime import datetime, timedelta, timezone
8
8
  import uuid
9
+
10
+ from .models.transaction import Transaction
9
11
  from .format import encode, decode
10
12
  from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
11
13
  from cryptography.hazmat.primitives import serialization
@@ -324,181 +326,6 @@ class Env:
324
326
  )
325
327
 
326
328
 
327
- class Transaction:
328
- def __init__(
329
- self,
330
- sender_pk: bytes,
331
- recipient_pk: bytes,
332
- amount: int,
333
- fee: int,
334
- nonce: int,
335
- signature: bytes | None = None,
336
- ) -> None:
337
- self.sender_pk = sender_pk
338
- self.recipient_pk = recipient_pk
339
- self.amount = amount
340
- self.fee = fee
341
- self.nonce = nonce
342
- self.signature = signature
343
-
344
- if self.amount < 0 or self.fee < 0:
345
- raise ValueError("amount and fee must be non-negative")
346
-
347
- if self.fee % 2 != 0:
348
- raise ValueError("fee must be divisible by two")
349
-
350
- self.tx_body_hash: bytes = blake3.blake3(self._body_bytes()).digest()
351
-
352
- if self.signature is not None:
353
- self.tx_hash = blake3.blake3(self.tx_body_hash + self.signature).digest()
354
- else:
355
- self.tx_hash = None
356
-
357
- def sign(self, priv_key: ed25519.Ed25519PrivateKey) -> None:
358
- if self.signature is not None:
359
- raise ValueError("transaction already signed")
360
- sig = priv_key.sign(self.tx_body_hash)
361
- self.signature = sig
362
- self.tx_hash = blake3.blake3(self.tx_body_hash + sig).digest()
363
-
364
- def verify_signature(self) -> bool:
365
- if self.signature is None:
366
- return False
367
- try:
368
- pub = ed25519.Ed25519PublicKey.from_public_bytes(self.sender_pk)
369
- pub.verify(self.signature, self.tx_body_hash)
370
- return True
371
- except Exception:
372
- return False
373
-
374
- def to_bytes(self) -> bytes:
375
- sig = self.signature or b""
376
- return encode([
377
- self.sender_pk,
378
- self.recipient_pk,
379
- self.amount,
380
- self.fee,
381
- self.nonce,
382
- sig,
383
- ])
384
-
385
- @classmethod
386
- def from_bytes(cls, blob: bytes) -> 'Transaction':
387
- sender, recipient, amount, fee, nonce, sig = decode(blob)
388
- return cls(sender, recipient, int(amount), int(fee), int(nonce), sig)
389
-
390
- def _body_bytes(self) -> bytes:
391
- return encode([
392
- self.sender_pk,
393
- self.recipient_pk,
394
- self.amount,
395
- self.fee,
396
- self.nonce,
397
- ])
398
-
399
- def __eq__(self, other: Any) -> bool:
400
- if not isinstance(other, Transaction):
401
- return NotImplemented
402
- return self.tx_hash == other.tx_hash
403
-
404
- def __hash__(self) -> int:
405
- return int.from_bytes(self.tx_hash, 'big')
406
-
407
- class Block:
408
- def __init__(
409
- self,
410
- *,
411
- number: int,
412
- prev_block_hash: bytes,
413
- timestamp: int,
414
- accounts_hash: bytes,
415
- total_astre_burned: int,
416
- tx_limit: int,
417
- tx_root: bytes,
418
- vdf_difficulty: int,
419
- vdf_output: bytes,
420
- vdf_proof: bytes,
421
- validator_pk: bytes,
422
- signature: bytes,
423
- ) -> None:
424
- self.accounts_hash = accounts_hash
425
- self.number = int(number)
426
- self.prev_block_hash = prev_block_hash
427
- self.timestamp = int(timestamp)
428
- self.total_astre_burned = int(total_astre_burned)
429
- self.tx_limit = int(tx_limit)
430
- self.tx_root = tx_root
431
- self.validator_pk = validator_pk
432
- self.vdf_difficulty = int(vdf_difficulty)
433
- self.vdf_output = vdf_output
434
- self.vdf_proof = vdf_proof
435
- self.signature = signature
436
- self.body_hash = self._compute_body_hash()
437
-
438
- def _body_fields_without_sig(self) -> list:
439
- return [
440
- self.accounts_hash,
441
- self.number,
442
- self.prev_block_hash,
443
- self.timestamp,
444
- self.total_astre_burned,
445
- self.tx_limit,
446
- self.tx_root,
447
- self.validator_pk,
448
- self.vdf_difficulty,
449
- self.vdf_output,
450
- self.vdf_proof,
451
- ]
452
-
453
- def _compute_body_hash(self) -> bytes:
454
- return blake3.blake3(encode(self._body_fields_without_sig())).digest()
455
-
456
- def to_bytes(self) -> bytes:
457
- return encode(self._body_fields_without_sig() + [self.signature])
458
-
459
- @classmethod
460
- def from_bytes(cls, blob: bytes) -> "Block":
461
- (
462
- accounts_hash,
463
- number,
464
- prev_block_hash,
465
- timestamp,
466
- total_astre_burned,
467
- tx_limit,
468
- tx_root,
469
- validator_pk,
470
- vdf_difficulty,
471
- vdf_output,
472
- vdf_proof,
473
- signature
474
- ) = decode(blob)
475
- return cls(
476
- number=int(number),
477
- prev_block_hash=prev_block_hash,
478
- timestamp=int(timestamp),
479
- accounts_hash=accounts_hash,
480
- total_astre_burned=int(total_astre_burned),
481
- tx_limit=int(tx_limit),
482
- tx_root=tx_root,
483
- vdf_difficulty=int(vdf_difficulty),
484
- vdf_output=vdf_output,
485
- vdf_proof=vdf_proof,
486
- validator_pk=validator_pk,
487
- signature=signature,
488
- )
489
-
490
- @property
491
- def hash(self) -> bytes:
492
- return blake3.blake3(self.body_hash + self.signature).digest()
493
-
494
- def verify_block_signature(self) -> bool:
495
- try:
496
- pub = ed25519.Ed25519PublicKey.from_public_bytes(self.validator_pk)
497
- pub.verify(self.signature, self.body_hash)
498
- return True
499
- except Exception:
500
- return False
501
-
502
329
  class Node:
503
330
  def __init__(self, config: dict = {}):
504
331
  self._machine_setup()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
5
5
  Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
6
6
  Project-URL: Homepage, https://github.com/astreum/lib
@@ -118,6 +118,6 @@ except ParseError as e:
118
118
  ## Testing
119
119
 
120
120
  ```bash
121
-
121
+ source venv/bin/activate
122
122
  python3 -m unittest discover -s tests
123
123
  ```
@@ -21,6 +21,9 @@ src/astreum/crypto/x25519.py
21
21
  src/astreum/lispeum/__init__.py
22
22
  src/astreum/lispeum/parser.py
23
23
  src/astreum/lispeum/tokenizer.py
24
- src/astreum/utils/__init__.py
25
- src/astreum/utils/patricia.py
24
+ src/astreum/models/__init__.py
25
+ src/astreum/models/block.py
26
+ src/astreum/models/merkle.py
27
+ src/astreum/models/patricia.py
28
+ src/astreum/models/transaction.py
26
29
  tests/test_node_machine.py
File without changes
File without changes
File without changes