astreum 0.2.13__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.
- {astreum-0.2.13/src/astreum.egg-info → astreum-0.2.14}/PKG-INFO +1 -1
- {astreum-0.2.13 → astreum-0.2.14}/pyproject.toml +1 -1
- astreum-0.2.14/src/astreum/models/block.py +98 -0
- astreum-0.2.14/src/astreum/models/merkle.py +233 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/node.py +0 -94
- {astreum-0.2.13 → astreum-0.2.14/src/astreum.egg-info}/PKG-INFO +1 -1
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum.egg-info/SOURCES.txt +2 -0
- {astreum-0.2.13 → astreum-0.2.14}/LICENSE +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/README.md +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/setup.cfg +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/_node/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/_node/storage/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/_node/storage/merkle.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/_node/storage/patricia.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/crypto/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/crypto/ed25519.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/crypto/quadratic_form.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/crypto/wesolowski.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/crypto/x25519.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/format.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/lispeum/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/lispeum/parser.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/lispeum/tokenizer.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/models/__init__.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/models/patricia.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum/models/transaction.py +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum.egg-info/dependency_links.txt +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum.egg-info/requires.txt +0 -0
- {astreum-0.2.13 → astreum-0.2.14}/src/astreum.egg-info/top_level.txt +0 -0
- {astreum-0.2.13 → 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.
|
|
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
|
|
@@ -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
|
+
|
|
@@ -325,101 +325,7 @@ class Env:
|
|
|
325
325
|
f"parent_id={self.parent_id})"
|
|
326
326
|
)
|
|
327
327
|
|
|
328
|
-
class Block:
|
|
329
|
-
def __init__(
|
|
330
|
-
self,
|
|
331
|
-
*,
|
|
332
|
-
number: int,
|
|
333
|
-
prev_block_hash: bytes,
|
|
334
|
-
timestamp: int,
|
|
335
|
-
accounts_hash: bytes,
|
|
336
|
-
total_astre_burned: int,
|
|
337
|
-
tx_limit: int,
|
|
338
|
-
tx_root: bytes,
|
|
339
|
-
vdf_difficulty: int,
|
|
340
|
-
vdf_output: bytes,
|
|
341
|
-
vdf_proof: bytes,
|
|
342
|
-
validator_pk: bytes,
|
|
343
|
-
signature: bytes,
|
|
344
|
-
) -> None:
|
|
345
|
-
self.accounts_hash = accounts_hash
|
|
346
|
-
self.number = int(number)
|
|
347
|
-
self.prev_block_hash = prev_block_hash
|
|
348
|
-
self.timestamp = int(timestamp)
|
|
349
|
-
self.total_astre_burned = int(total_astre_burned)
|
|
350
|
-
self.tx_limit = int(tx_limit)
|
|
351
|
-
self.tx_root = tx_root
|
|
352
|
-
self.validator_pk = validator_pk
|
|
353
|
-
self.vdf_difficulty = int(vdf_difficulty)
|
|
354
|
-
self.vdf_output = vdf_output
|
|
355
|
-
self.vdf_proof = vdf_proof
|
|
356
|
-
self.signature = signature
|
|
357
|
-
self.body_hash = self._compute_body_hash()
|
|
358
|
-
|
|
359
|
-
def _body_fields_without_sig(self) -> list:
|
|
360
|
-
return [
|
|
361
|
-
self.accounts_hash,
|
|
362
|
-
self.number,
|
|
363
|
-
self.prev_block_hash,
|
|
364
|
-
self.timestamp,
|
|
365
|
-
self.total_astre_burned,
|
|
366
|
-
self.tx_limit,
|
|
367
|
-
self.tx_root,
|
|
368
|
-
self.validator_pk,
|
|
369
|
-
self.vdf_difficulty,
|
|
370
|
-
self.vdf_output,
|
|
371
|
-
self.vdf_proof,
|
|
372
|
-
]
|
|
373
|
-
|
|
374
|
-
def _compute_body_hash(self) -> bytes:
|
|
375
|
-
return blake3.blake3(encode(self._body_fields_without_sig())).digest()
|
|
376
|
-
|
|
377
|
-
def to_bytes(self) -> bytes:
|
|
378
|
-
return encode(self._body_fields_without_sig() + [self.signature])
|
|
379
|
-
|
|
380
|
-
@classmethod
|
|
381
|
-
def from_bytes(cls, blob: bytes) -> "Block":
|
|
382
|
-
(
|
|
383
|
-
accounts_hash,
|
|
384
|
-
number,
|
|
385
|
-
prev_block_hash,
|
|
386
|
-
timestamp,
|
|
387
|
-
total_astre_burned,
|
|
388
|
-
tx_limit,
|
|
389
|
-
tx_root,
|
|
390
|
-
validator_pk,
|
|
391
|
-
vdf_difficulty,
|
|
392
|
-
vdf_output,
|
|
393
|
-
vdf_proof,
|
|
394
|
-
signature
|
|
395
|
-
) = decode(blob)
|
|
396
|
-
return cls(
|
|
397
|
-
number=int(number),
|
|
398
|
-
prev_block_hash=prev_block_hash,
|
|
399
|
-
timestamp=int(timestamp),
|
|
400
|
-
accounts_hash=accounts_hash,
|
|
401
|
-
total_astre_burned=int(total_astre_burned),
|
|
402
|
-
tx_limit=int(tx_limit),
|
|
403
|
-
tx_root=tx_root,
|
|
404
|
-
vdf_difficulty=int(vdf_difficulty),
|
|
405
|
-
vdf_output=vdf_output,
|
|
406
|
-
vdf_proof=vdf_proof,
|
|
407
|
-
validator_pk=validator_pk,
|
|
408
|
-
signature=signature,
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
@property
|
|
412
|
-
def hash(self) -> bytes:
|
|
413
|
-
return blake3.blake3(self.body_hash + self.signature).digest()
|
|
414
328
|
|
|
415
|
-
def verify_block_signature(self) -> bool:
|
|
416
|
-
try:
|
|
417
|
-
pub = ed25519.Ed25519PublicKey.from_public_bytes(self.validator_pk)
|
|
418
|
-
pub.verify(self.signature, self.body_hash)
|
|
419
|
-
return True
|
|
420
|
-
except Exception:
|
|
421
|
-
return False
|
|
422
|
-
|
|
423
329
|
class Node:
|
|
424
330
|
def __init__(self, config: dict = {}):
|
|
425
331
|
self._machine_setup()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.2.
|
|
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
|
|
@@ -22,6 +22,8 @@ src/astreum/lispeum/__init__.py
|
|
|
22
22
|
src/astreum/lispeum/parser.py
|
|
23
23
|
src/astreum/lispeum/tokenizer.py
|
|
24
24
|
src/astreum/models/__init__.py
|
|
25
|
+
src/astreum/models/block.py
|
|
26
|
+
src/astreum/models/merkle.py
|
|
25
27
|
src/astreum/models/patricia.py
|
|
26
28
|
src/astreum/models/transaction.py
|
|
27
29
|
tests/test_node_machine.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|