astreum 0.2.25__tar.gz → 0.2.26__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.
Files changed (29) hide show
  1. {astreum-0.2.25/src/astreum.egg-info → astreum-0.2.26}/PKG-INFO +1 -1
  2. {astreum-0.2.25 → astreum-0.2.26}/pyproject.toml +1 -1
  3. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/block.py +107 -22
  4. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/merkle.py +6 -6
  5. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/transaction.py +3 -3
  6. {astreum-0.2.25 → astreum-0.2.26/src/astreum.egg-info}/PKG-INFO +1 -1
  7. {astreum-0.2.25 → astreum-0.2.26}/LICENSE +0 -0
  8. {astreum-0.2.25 → astreum-0.2.26}/README.md +0 -0
  9. {astreum-0.2.25 → astreum-0.2.26}/setup.cfg +0 -0
  10. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/__init__.py +0 -0
  11. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/crypto/__init__.py +0 -0
  12. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/crypto/ed25519.py +0 -0
  13. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/crypto/quadratic_form.py +0 -0
  14. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/crypto/wesolowski.py +0 -0
  15. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/crypto/x25519.py +0 -0
  16. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/format.py +0 -0
  17. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/lispeum/__init__.py +0 -0
  18. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/lispeum/parser.py +0 -0
  19. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/lispeum/tokenizer.py +0 -0
  20. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/__init__.py +0 -0
  21. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/account.py +0 -0
  22. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/accounts.py +0 -0
  23. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/models/patricia.py +0 -0
  24. {astreum-0.2.25 → astreum-0.2.26}/src/astreum/node.py +0 -0
  25. {astreum-0.2.25 → astreum-0.2.26}/src/astreum.egg-info/SOURCES.txt +0 -0
  26. {astreum-0.2.25 → astreum-0.2.26}/src/astreum.egg-info/dependency_links.txt +0 -0
  27. {astreum-0.2.25 → astreum-0.2.26}/src/astreum.egg-info/requires.txt +0 -0
  28. {astreum-0.2.25 → astreum-0.2.26}/src/astreum.egg-info/top_level.txt +0 -0
  29. {astreum-0.2.25 → astreum-0.2.26}/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.25
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astreum"
3
- version = "0.2.25"
3
+ version = "0.2.26"
4
4
  authors = [
5
5
  { name="Roy R. O. Okello", email="roy@stelar.xyz" },
6
6
  ]
@@ -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. validator-stakes sub-trie
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. build the two Account bodies
108
- validator_acct = Account.create(balance=0, data=b"", nonce=0)
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. global Accounts structure
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. constant body fields for genesis
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 = 0,
118
+ transaction_limit = 1,
125
119
  transactions_root_hash = b"\x00" * 32,
126
- delay_difficulty = 0,
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. build and return the block
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, # private key for signing
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
+
@@ -41,11 +41,11 @@ class MerkleNode:
41
41
  class MerkleTree:
42
42
  def __init__(
43
43
  self,
44
- node_get: Callable[[bytes], Optional[bytes]],
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._node_get = node_get
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
- node_get: Callable[[bytes], Optional[bytes]] | None = None,
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
- node_get = node_get or (lambda _h: None)
63
- tree = cls(node_get=node_get)
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._node_get(h)
100
+ raw = self._global_get_fn(h)
101
101
  if raw is None:
102
102
  return None
103
103
  node = MerkleNode.from_bytes(raw)
@@ -27,14 +27,14 @@ class Transaction:
27
27
  tx_hash: bytes,
28
28
  *,
29
29
  tree: Optional[MerkleTree] = None,
30
- get_node_fn: Optional[Callable[[bytes], Optional[bytes]]] = None,
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 get_node_fn:
37
- self._tree.set_external_node_fetcher(get_node_fn)
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.25
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
File without changes
File without changes
File without changes
File without changes
File without changes