astreum 0.2.25__py3-none-any.whl → 0.2.26__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.
Potentially problematic release.
This version of astreum might be problematic. Click here for more details.
- astreum/models/block.py +107 -22
- astreum/models/merkle.py +6 -6
- astreum/models/transaction.py +3 -3
- {astreum-0.2.25.dist-info → astreum-0.2.26.dist-info}/METADATA +1 -1
- {astreum-0.2.25.dist-info → astreum-0.2.26.dist-info}/RECORD +8 -8
- {astreum-0.2.25.dist-info → astreum-0.2.26.dist-info}/WHEEL +0 -0
- {astreum-0.2.25.dist-info → astreum-0.2.26.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.2.25.dist-info → astreum-0.2.26.dist-info}/top_level.txt +0 -0
astreum/models/block.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from threading import Thread
|
|
4
4
|
from typing import List, Dict, Any, Optional, Union
|
|
5
5
|
|
|
6
|
-
from astreum.crypto.wesolowski import vdf_generate
|
|
6
|
+
from astreum.crypto.wesolowski import vdf_generate, vdf_verify
|
|
7
7
|
from astreum.models.account import Account
|
|
8
8
|
from astreum.models.accounts import Accounts
|
|
9
9
|
from astreum.models.patricia import PatriciaTrie
|
|
@@ -86,51 +86,46 @@ class Block:
|
|
|
86
86
|
return int.from_bytes(leaf_bytes, "big")
|
|
87
87
|
return leaf_bytes
|
|
88
88
|
|
|
89
|
-
def verify_block_signature(self) -> bool:
|
|
90
|
-
"""Verify the block's Ed25519 signature against its body root."""
|
|
91
|
-
pub = ed25519.Ed25519PublicKey.from_public_bytes(
|
|
92
|
-
self.get_field("validator_pk")
|
|
93
|
-
)
|
|
94
|
-
try:
|
|
95
|
-
pub.verify(self.get_signature(), self.get_body_hash())
|
|
96
|
-
return True
|
|
97
|
-
except Exception:
|
|
98
|
-
return False
|
|
99
|
-
|
|
100
89
|
@classmethod
|
|
101
90
|
def genesis(cls, validator_addr: bytes) -> "Block":
|
|
102
|
-
# 1
|
|
91
|
+
# 1. validator-stakes sub-trie
|
|
103
92
|
stake_trie = PatriciaTrie()
|
|
104
93
|
stake_trie.put(validator_addr, (1).to_bytes(32, "big"))
|
|
105
94
|
stake_root = stake_trie.root_hash
|
|
106
95
|
|
|
107
|
-
# 2
|
|
108
|
-
validator_acct = Account.create(balance=0, data=b"",
|
|
96
|
+
# 2. three Account bodies
|
|
97
|
+
validator_acct = Account.create(balance=0, data=b"", nonce=0)
|
|
109
98
|
treasury_acct = Account.create(balance=1, data=stake_root, nonce=0)
|
|
99
|
+
burn_acct = Account.create(balance=0, data=b"", nonce=0)
|
|
110
100
|
|
|
111
|
-
# 3
|
|
101
|
+
# 3. global Accounts structure
|
|
112
102
|
accts = Accounts()
|
|
113
103
|
accts.set_account(validator_addr, validator_acct)
|
|
114
104
|
accts.set_account(b"\x11" * 32, treasury_acct)
|
|
105
|
+
accts.set_account(b"\x00" * 32, burn_acct)
|
|
115
106
|
accounts_hash = accts.root_hash
|
|
116
107
|
|
|
117
|
-
# 4
|
|
108
|
+
# 4. constant body fields for genesis
|
|
118
109
|
body_kwargs = dict(
|
|
110
|
+
block_hash = b"",
|
|
119
111
|
number = 0,
|
|
120
112
|
prev_block_hash = b"\x00" * 32,
|
|
121
113
|
timestamp = 0,
|
|
114
|
+
block_time = 0,
|
|
122
115
|
accounts_hash = accounts_hash,
|
|
116
|
+
accounts = accts,
|
|
123
117
|
transactions_total_fees = 0,
|
|
124
|
-
transaction_limit =
|
|
118
|
+
transaction_limit = 1,
|
|
125
119
|
transactions_root_hash = b"\x00" * 32,
|
|
126
|
-
|
|
120
|
+
transactions_count = 0,
|
|
121
|
+
delay_difficulty = 1,
|
|
127
122
|
delay_output = b"",
|
|
128
123
|
delay_proof = b"",
|
|
129
124
|
validator_pk = validator_addr,
|
|
130
125
|
signature = b"",
|
|
131
126
|
)
|
|
132
127
|
|
|
133
|
-
# 5
|
|
128
|
+
# 5. build and return the block
|
|
134
129
|
return cls.create(**body_kwargs)
|
|
135
130
|
|
|
136
131
|
@classmethod
|
|
@@ -139,10 +134,9 @@ class Block:
|
|
|
139
134
|
previous_block: "Block",
|
|
140
135
|
transactions: List[Transaction],
|
|
141
136
|
*,
|
|
142
|
-
validator_sk,
|
|
137
|
+
validator_sk,
|
|
143
138
|
natural_rate: float = 0.618,
|
|
144
139
|
) -> "Block":
|
|
145
|
-
TREASURY = b"\x11" * 32
|
|
146
140
|
BURN = b"\x00" * 32
|
|
147
141
|
|
|
148
142
|
blk = cls(
|
|
@@ -345,3 +339,94 @@ class Block:
|
|
|
345
339
|
self.total_fees += fee
|
|
346
340
|
self.tx_hashes.append(tx.hash)
|
|
347
341
|
self.transactions_count += 1
|
|
342
|
+
|
|
343
|
+
def validate_block(self, remote_get_fn) -> bool:
|
|
344
|
+
NAT = 0.618
|
|
345
|
+
_i2b = lambda i: i.to_bytes((i.bit_length() + 7) // 8 or 1, "big")
|
|
346
|
+
|
|
347
|
+
# ---------- 1. block-hash & signature -----------------------------
|
|
348
|
+
blk_mt = MerkleTree(node_get=remote_get_fn, root_hash=self.hash)
|
|
349
|
+
body_root = blk_mt.get(0); sig = blk_mt.get(1)
|
|
350
|
+
ed25519.verify_signature(public_key=self.validator_pk, message=body_root, signature=sig)
|
|
351
|
+
|
|
352
|
+
# ---------- 2. rebuild body_root from fields ----------------------
|
|
353
|
+
f_names = (
|
|
354
|
+
"accounts_hash","block_time","delay_difficulty","delay_output","delay_proof",
|
|
355
|
+
"number","prev_block_hash","timestamp","transaction_limit",
|
|
356
|
+
"transactions_count","transactions_root_hash","transactions_total_fees",
|
|
357
|
+
"validator_pk",
|
|
358
|
+
)
|
|
359
|
+
leaves = [
|
|
360
|
+
v if isinstance(v := self.get_field(n), bytes) else _i2b(v)
|
|
361
|
+
for n in sorted(f_names)
|
|
362
|
+
]
|
|
363
|
+
if MerkleTree.from_leaves(leaves).root_hash != body_root:
|
|
364
|
+
raise ValueError("body root mismatch")
|
|
365
|
+
|
|
366
|
+
# ---------- 3. previous block header & VDF ------------------------
|
|
367
|
+
prev_mt = MerkleTree(node_get=remote_get_fn, root_hash=self.prev_block_hash)
|
|
368
|
+
prev_body_root, prev_sig = prev_mt.get(0), prev_mt.get(1)
|
|
369
|
+
prev_body_mt = MerkleTree(node_get=remote_get_fn, root_hash=prev_body_root)
|
|
370
|
+
prev_blk = Block(block_hash=self.prev_block_hash,
|
|
371
|
+
body_tree=prev_body_mt, signature=prev_sig)
|
|
372
|
+
prev_out = prev_blk.get_field("delay_output")
|
|
373
|
+
prev_diff = prev_blk.get_field("delay_difficulty")
|
|
374
|
+
prev_bt = prev_blk.get_field("block_time")
|
|
375
|
+
prev_limit = prev_blk.get_field("transaction_limit")
|
|
376
|
+
prev_cnt = prev_blk.get_field("transactions_count")
|
|
377
|
+
|
|
378
|
+
if not vdf_verify(prev_out, self.delay_output, self.delay_proof,
|
|
379
|
+
T=self.delay_difficulty, D=-4):
|
|
380
|
+
raise ValueError("bad VDF proof")
|
|
381
|
+
|
|
382
|
+
# ---------- 4. replay all txs -------------------------------------
|
|
383
|
+
accs = Accounts(root_hash=prev_blk.get_field("accounts_hash"),
|
|
384
|
+
node_get=remote_get_fn)
|
|
385
|
+
tx_mt = MerkleTree(node_get=remote_get_fn,
|
|
386
|
+
root_hash=self.transactions_root_hash)
|
|
387
|
+
if tx_mt.leaf_count() != self.transactions_count:
|
|
388
|
+
raise ValueError("transactions_count mismatch")
|
|
389
|
+
|
|
390
|
+
dummy = Block(block_hash=b"", accounts=accs,
|
|
391
|
+
accounts_hash=accs.root_hash,
|
|
392
|
+
transaction_limit=prev_limit)
|
|
393
|
+
for i in range(self.transactions_count):
|
|
394
|
+
h = tx_mt.get(i)
|
|
395
|
+
tm = MerkleTree(node_get=remote_get_fn, root_hash=h)
|
|
396
|
+
tx = Transaction(h, tree=tm, node_get=remote_get_fn)
|
|
397
|
+
dummy.apply_tx(tx)
|
|
398
|
+
|
|
399
|
+
# fee split identical to build()
|
|
400
|
+
burn = dummy.total_fees // 2
|
|
401
|
+
rew = dummy.total_fees - burn
|
|
402
|
+
if burn:
|
|
403
|
+
dummy.accounts.set_account(
|
|
404
|
+
b"\x00"*32,
|
|
405
|
+
Account.create(burn, b"", 0)
|
|
406
|
+
)
|
|
407
|
+
if rew:
|
|
408
|
+
v_acct = dummy.accounts.get_account(self.validator_pk) or Account.create(0,b"",0)
|
|
409
|
+
dummy.accounts.set_account(
|
|
410
|
+
self.validator_pk,
|
|
411
|
+
Account.create(v_acct.balance()+rew, v_acct.data(), v_acct.nonce())
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
if dummy.accounts.root_hash != self.accounts_hash:
|
|
415
|
+
raise ValueError("accounts_hash mismatch")
|
|
416
|
+
|
|
417
|
+
# ---------- 5. natural-rate rules --------------------------------
|
|
418
|
+
grow_thr = prev_limit * NAT
|
|
419
|
+
shrink_thr = prev_cnt * NAT
|
|
420
|
+
expect_lim = prev_cnt if prev_cnt > grow_thr \
|
|
421
|
+
else max(1, int(prev_limit * NAT)) if prev_cnt < shrink_thr \
|
|
422
|
+
else prev_limit
|
|
423
|
+
if self.transaction_limit != expect_lim:
|
|
424
|
+
raise ValueError("tx-limit rule")
|
|
425
|
+
|
|
426
|
+
expect_diff = max(1, int(prev_diff / NAT)) if prev_bt <= 1 \
|
|
427
|
+
else max(1, int(prev_diff * NAT))
|
|
428
|
+
if self.delay_difficulty != expect_diff:
|
|
429
|
+
raise ValueError("difficulty rule")
|
|
430
|
+
|
|
431
|
+
return True
|
|
432
|
+
|
astreum/models/merkle.py
CHANGED
|
@@ -41,11 +41,11 @@ class MerkleNode:
|
|
|
41
41
|
class MerkleTree:
|
|
42
42
|
def __init__(
|
|
43
43
|
self,
|
|
44
|
-
|
|
44
|
+
global_get_fn: Callable[[bytes], Optional[bytes]],
|
|
45
45
|
root_hash: Optional[bytes] = None,
|
|
46
46
|
height: Optional[int] = None,
|
|
47
47
|
) -> None:
|
|
48
|
-
self.
|
|
48
|
+
self._global_get_fn = global_get_fn
|
|
49
49
|
self.nodes: Dict[bytes, MerkleNode] = {}
|
|
50
50
|
self.root_hash = root_hash
|
|
51
51
|
self._height: Optional[int] = height
|
|
@@ -54,13 +54,13 @@ class MerkleTree:
|
|
|
54
54
|
def from_leaves(
|
|
55
55
|
cls,
|
|
56
56
|
leaves: List[bytes],
|
|
57
|
-
|
|
57
|
+
global_get_fn: Callable[[bytes], Optional[bytes]] | None = None,
|
|
58
58
|
) -> "MerkleTree":
|
|
59
59
|
if not leaves:
|
|
60
60
|
raise ValueError("must supply at least one leaf")
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
tree = cls(
|
|
62
|
+
global_get_fn = global_get_fn or (lambda _h: None)
|
|
63
|
+
tree = cls(global_get_fn=global_get_fn)
|
|
64
64
|
|
|
65
65
|
# Step 1 – create leaf nodes list[bytes]
|
|
66
66
|
level_hashes: List[bytes] = []
|
|
@@ -97,7 +97,7 @@ class MerkleTree:
|
|
|
97
97
|
return None
|
|
98
98
|
node = self.nodes.get(h)
|
|
99
99
|
if node is None:
|
|
100
|
-
raw = self.
|
|
100
|
+
raw = self._global_get_fn(h)
|
|
101
101
|
if raw is None:
|
|
102
102
|
return None
|
|
103
103
|
node = MerkleNode.from_bytes(raw)
|
astreum/models/transaction.py
CHANGED
|
@@ -27,14 +27,14 @@ class Transaction:
|
|
|
27
27
|
tx_hash: bytes,
|
|
28
28
|
*,
|
|
29
29
|
tree: Optional[MerkleTree] = None,
|
|
30
|
-
|
|
30
|
+
global_get_fn: Optional[Callable[[bytes], Optional[bytes]]] = None,
|
|
31
31
|
) -> None:
|
|
32
32
|
self._hash = tx_hash
|
|
33
33
|
self._tree = tree
|
|
34
34
|
self._field_cache: Dict[str, Union[int, bytes]] = {}
|
|
35
35
|
|
|
36
|
-
if self._tree and
|
|
37
|
-
self._tree.
|
|
36
|
+
if self._tree and global_get_fn:
|
|
37
|
+
self._tree.global_get_fn = global_get_fn
|
|
38
38
|
|
|
39
39
|
@classmethod
|
|
40
40
|
def create(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.26
|
|
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
|
|
@@ -12,12 +12,12 @@ astreum/lispeum/tokenizer.py,sha256=J-I7MEd0r2ZoVqxvRPlu-Afe2ZdM0tKXXhf1R4SxYTo,
|
|
|
12
12
|
astreum/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
astreum/models/account.py,sha256=sHujGSwtV13rvOGJ5LZXuMrJ4F9XUdvyuWKz-zJ9lkE,2986
|
|
14
14
|
astreum/models/accounts.py,sha256=aFSEWlq6zRf65-KGAdNGqEJyNVY3fpKhx8y1vU6sgSc,1164
|
|
15
|
-
astreum/models/block.py,sha256
|
|
16
|
-
astreum/models/merkle.py,sha256=
|
|
15
|
+
astreum/models/block.py,sha256=-5j7uO0woVtNi0h52__e7AxpDQSVhzKUhr6Qc-2xZsE,17870
|
|
16
|
+
astreum/models/merkle.py,sha256=lvWJa9nmrBL0n_2h_uNqpB_9a5s5Hn1FceRLx0IZIVQ,6778
|
|
17
17
|
astreum/models/patricia.py,sha256=ohmXrcaz7Ae561tyC4u4iPOkQPkKr8N0IWJek4upFIg,13392
|
|
18
|
-
astreum/models/transaction.py,sha256=
|
|
19
|
-
astreum-0.2.
|
|
20
|
-
astreum-0.2.
|
|
21
|
-
astreum-0.2.
|
|
22
|
-
astreum-0.2.
|
|
23
|
-
astreum-0.2.
|
|
18
|
+
astreum/models/transaction.py,sha256=MkLL5YX18kIf9-O4LBaZ4eWjkXDAaYIrDcDehbDZoqg,3038
|
|
19
|
+
astreum-0.2.26.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
|
|
20
|
+
astreum-0.2.26.dist-info/METADATA,sha256=fUG5PepefY7Cjfp5Gj5GwtbYF369bARQAcl0A4OJiQ0,5478
|
|
21
|
+
astreum-0.2.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
astreum-0.2.26.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
|
|
23
|
+
astreum-0.2.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|