astreum 0.2.16__py3-none-any.whl → 0.2.17__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 CHANGED
@@ -1,10 +1,51 @@
1
- from ..format import encode, decode
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Dict, Any, Optional, Union
2
4
  from ..crypto import ed25519
3
- import blake3
5
+ from .merkle import MerkleTree
6
+
7
+ # Constants for integer field names
8
+ _INT_FIELDS = {
9
+ "delay_difficulty",
10
+ "number",
11
+ "timestamp",
12
+ "transaction_limit",
13
+ "transactions_total_fees",
14
+ }
4
15
 
5
16
  class Block:
6
17
  def __init__(
7
18
  self,
19
+ block_hash: bytes,
20
+ body_tree: Optional[MerkleTree] = None,
21
+ signature: Optional[bytes] = None,
22
+ ) -> None:
23
+ self._block_hash = block_hash
24
+ self._body_tree = body_tree
25
+ self._signature = signature
26
+ # store field names in alphabetical order for consistent indexing
27
+ self._field_names = [
28
+ "accounts_hash",
29
+ "delay_difficulty",
30
+ "delay_output",
31
+ "delay_proof",
32
+ "number",
33
+ "prev_block_hash",
34
+ "timestamp",
35
+ "transaction_limit",
36
+ "transactions_root_hash",
37
+ "transactions_total_fees",
38
+ "validator_pk",
39
+ ]
40
+
41
+ @property
42
+ def hash(self) -> bytes:
43
+ """Return the block hash (Merkle root of body_root || signature)."""
44
+ return self._block_hash
45
+
46
+ @classmethod
47
+ def create(
48
+ cls,
8
49
  number: int,
9
50
  prev_block_hash: bytes,
10
51
  timestamp: int,
@@ -12,87 +53,78 @@ class Block:
12
53
  transactions_total_fees: int,
13
54
  transaction_limit: int,
14
55
  transactions_root_hash: bytes,
15
- vdf_difficulty: int,
16
- vdf_output: bytes,
17
- vdf_proof: bytes,
56
+ delay_difficulty: int,
57
+ delay_output: bytes,
58
+ delay_proof: bytes,
18
59
  validator_pk: bytes,
19
60
  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()
61
+ ) -> Block:
62
+ """Build a new block by hashing the provided fields into Merkle trees."""
63
+ # map fields by name
64
+ field_map: Dict[str, Any] = {
65
+ "accounts_hash": accounts_hash,
66
+ "delay_difficulty": delay_difficulty,
67
+ "delay_output": delay_output,
68
+ "delay_proof": delay_proof,
69
+ "number": number,
70
+ "prev_block_hash": prev_block_hash,
71
+ "timestamp": timestamp,
72
+ "transaction_limit": transaction_limit,
73
+ "transactions_root_hash": transactions_root_hash,
74
+ "transactions_total_fees": transactions_total_fees,
75
+ "validator_pk": validator_pk,
76
+ }
77
+
78
+ leaves: List[bytes] = []
79
+ for name in sorted(field_map):
80
+ v = field_map[name]
81
+ if isinstance(v, bytes):
82
+ leaf_bytes = v
83
+ elif isinstance(v, int):
84
+ length = (v.bit_length() + 7) // 8 or 1
85
+ leaf_bytes = v.to_bytes(length, "big")
86
+ else:
87
+ raise TypeError(f"Unsupported field type for '{name}': {type(v)}")
88
+ leaves.append(leaf_bytes)
89
+
90
+ body_tree = MerkleTree.from_leaves(leaves)
91
+ body_root = body_tree.root_hash
92
+ top_tree = MerkleTree.from_leaves([body_root, signature])
93
+ block_hash = top_tree.root_hash
34
94
 
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
- ]
95
+ return cls(block_hash, body_tree, signature)
49
96
 
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])
97
+ def get_body_hash(self) -> bytes:
98
+ """Return the Merkle root of the body fields."""
99
+ if not self._body_tree:
100
+ raise ValueError("Body tree not available for this block instance.")
101
+ return self._body_tree.root_hash
55
102
 
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
- )
103
+ def get_signature(self) -> bytes:
104
+ """Return the block's signature leaf."""
105
+ if self._signature is None:
106
+ raise ValueError("Signature not available for this block instance.")
107
+ return self._signature
86
108
 
87
- @property
88
- def hash(self) -> bytes:
89
- return blake3.blake3(self.body_hash + self.signature).digest()
109
+ def get_field(self, name: str) -> Union[int, bytes]:
110
+ """Query a single body field by name, returning an int or bytes."""
111
+ if name not in self._field_names:
112
+ raise KeyError(f"Unknown field: {name}")
113
+ if not self._body_tree:
114
+ raise ValueError("Body tree not available for field queries.")
115
+ idx = self._field_names.index(name)
116
+ leaf_bytes = self._body_tree.leaves[idx]
117
+ if name in _INT_FIELDS:
118
+ return int.from_bytes(leaf_bytes, "big")
119
+ return leaf_bytes
90
120
 
91
121
  def verify_block_signature(self) -> bool:
122
+ """Verify the block's Ed25519 signature against its body root."""
123
+ pub = ed25519.Ed25519PublicKey.from_public_bytes(
124
+ self.get_field("validator_pk")
125
+ )
92
126
  try:
93
- pub = ed25519.Ed25519PublicKey.from_public_bytes(self.validator_pk)
94
- pub.verify(self.signature, self.body_hash)
127
+ pub.verify(self.get_signature(), self.get_body_hash())
95
128
  return True
96
129
  except Exception:
97
130
  return False
98
-
@@ -74,10 +74,5 @@ class Transaction:
74
74
  self.nonce,
75
75
  ])
76
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
77
  def __hash__(self) -> int:
83
78
  return int.from_bytes(self.tx_hash, 'big')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.16
3
+ Version: 0.2.17
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
@@ -10,12 +10,12 @@ astreum/lispeum/__init__.py,sha256=K-NDzIjtIsXzC9X7lnYvlvIaVxjFcY7WNsgLIE3DH3U,5
10
10
  astreum/lispeum/parser.py,sha256=jQRzZYvBuSg8t_bxsbt1-WcHaR_LPveHNX7Qlxhaw-M,1165
11
11
  astreum/lispeum/tokenizer.py,sha256=J-I7MEd0r2ZoVqxvRPlu-Afe2ZdM0tKXXhf1R4SxYTo,1429
12
12
  astreum/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- astreum/models/block.py,sha256=NKYyxL6_BrtXRgcgIrnkYsobX0Z_bGqQT-fQ_09zEOo,3226
13
+ astreum/models/block.py,sha256=YIY3qLneFM7OooY29khLDEs3NIEA7y8VhlXitflkxTo,4564
14
14
  astreum/models/merkle.py,sha256=E850dITZmuQS2LmQt2yA_luy0a3QjHgSbL2ibQAmYJc,8627
15
15
  astreum/models/patricia.py,sha256=WtfwVufZazFTOjVDnSDI0I3ghetm3GBzAwqNQevOlJ8,12974
16
- astreum/models/transaction.py,sha256=Vu0cfmh80S31nEbxyJfv1dk9_zqtgGNyMdhlM0uQF4E,2611
17
- astreum-0.2.16.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
18
- astreum-0.2.16.dist-info/METADATA,sha256=u_O4mRcn4Ve_9zpgQQIatLYzF_R2K_jY-hxLQQ1HMLg,5478
19
- astreum-0.2.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- astreum-0.2.16.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
21
- astreum-0.2.16.dist-info/RECORD,,
16
+ astreum/models/transaction.py,sha256=ReCmLf9j1WLm2VUTtZh4cAVEyJodk17bVPp3t-UyNnI,2427
17
+ astreum-0.2.17.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
18
+ astreum-0.2.17.dist-info/METADATA,sha256=7BooYG3o3YQyZ2Y0hOsAtD-5ykT5nvfWnT3koADQ4gM,5478
19
+ astreum-0.2.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ astreum-0.2.17.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
21
+ astreum-0.2.17.dist-info/RECORD,,