astreum 0.2.25__py3-none-any.whl → 0.2.27__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
@@ -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
+
astreum/models/merkle.py CHANGED
@@ -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)
@@ -0,0 +1,64 @@
1
+ from enum import IntEnum
2
+ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
3
+
4
+ class MessageTopic(IntEnum):
5
+ PING = 0
6
+ OBJECT_REQUEST = 1
7
+ OBJECT_RESPONSE = 2
8
+ ROUTE_REQUEST = 3
9
+ ROUTE_RESPONSE = 4
10
+
11
+ class Message:
12
+ handshake: bool
13
+ sender: X25519PublicKey
14
+
15
+ topic: MessageTopic
16
+ content: bytes
17
+
18
+ def to_bytes(self):
19
+ if self.handshake:
20
+ # handshake byte (1) + raw public key bytes
21
+ return bytes([1]) + self.sender.public_bytes(
22
+ encoding=serialization.Encoding.Raw,
23
+ format=serialization.PublicFormat.Raw
24
+ )
25
+ else:
26
+ # normal message: 0 + topic + content
27
+ return bytes([0, self.topic.value]) + self.content
28
+
29
+ @classmethod
30
+ def from_bytes(cls, data: bytes) -> "Message":
31
+ if len(data) < 1:
32
+ raise ValueError("Cannot parse Message: no data")
33
+ flag = data[0]
34
+ # create empty instance
35
+ msg = cls.__new__(cls)
36
+
37
+ if flag == 1:
38
+ # handshake message: the rest is the peer’s public key
39
+ key_bytes = data[1:]
40
+ try:
41
+ sender = X25519PublicKey.from_public_bytes(key_bytes)
42
+ except ValueError:
43
+ raise ValueError("Invalid public key bytes")
44
+ msg.handshake = True
45
+ msg.sender = sender
46
+ msg.topic = None
47
+ msg.content = b''
48
+ elif flag == 0:
49
+ # normal message: next byte is topic, rest is content
50
+ if len(data) < 2:
51
+ raise ValueError("Cannot parse Message: missing topic byte")
52
+ topic_val = data[1]
53
+ try:
54
+ topic = MessageTopic(topic_val)
55
+ except ValueError:
56
+ raise ValueError(f"Unknown MessageTopic: {topic_val}")
57
+ msg.handshake = False
58
+ msg.sender = None
59
+ msg.topic = topic
60
+ msg.content = data[2:]
61
+ else:
62
+ raise ValueError(f"Invalid handshake flag: {flag}")
63
+
64
+ return msg
@@ -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(
astreum/node.py CHANGED
@@ -15,6 +15,7 @@ from .crypto import ed25519, x25519
15
15
  from enum import IntEnum
16
16
  import blake3
17
17
  import struct
18
+ from .models.message import Message, MessageTopic
18
19
 
19
20
  class ObjectRequestType(IntEnum):
20
21
  OBJECT_GET = 0
@@ -61,106 +62,11 @@ class ObjectResponse:
61
62
  type_val, data_val, hash_val = decode(data)
62
63
  return cls(type=ObjectResponseType(type_val[0]), data=data_val, hash=hash_val)
63
64
 
64
- class MessageTopic(IntEnum):
65
- PING = 0
66
- OBJECT_REQUEST = 1
67
- OBJECT_RESPONSE = 2
68
- ROUTE_REQUEST = 3
69
- ROUTE_RESPONSE = 4
70
-
71
- class Message:
72
- body: bytes
73
- topic: MessageTopic
74
-
75
- def to_bytes(self):
76
- return encode([self.body, [self.topic.value]])
77
-
78
- @classmethod
79
- def from_bytes(cls, data: bytes):
80
- body, topic = decode(data)
81
- return cls(body=body, topic=MessageTopic(topic[0]))
82
-
83
- class Envelope:
84
- encrypted: bool
85
- message: Message
86
- nonce: int
87
- sender: X25519PublicKey
88
- timestamp: datetime
89
-
90
- def __init__(self, message: Message, sender: X25519PublicKey, encrypted: bool = False, nonce: int = 0, timestamp: Union[int, datetime, None] = None, difficulty: int = 1):
91
- self.encrypted = encrypted
92
- encrypted_bytes = b'\x01' if self.encrypted else b'\x00'
93
-
94
- self.message = message
95
- message_bytes = message.to_bytes()
96
-
97
- self.sender = sender
98
- self.sender_bytes = sender.public_bytes()
99
-
100
- self.nonce = nonce
101
-
102
- if timestamp is None:
103
- self.timestamp = datetime.now(timezone.utc)
104
- timestamp_int = int(self.timestamp.timestamp())
105
- elif isinstance(timestamp, int):
106
- self.timestamp = datetime.fromtimestamp(timestamp, timezone.utc)
107
- timestamp_int = timestamp
108
- elif isinstance(timestamp, datetime):
109
- self.timestamp = timestamp
110
- timestamp_int = int(timestamp.timestamp())
111
- else:
112
- raise TypeError("Timestamp must be an int (Unix timestamp), datetime object, or None")
113
-
114
- def count_leading_zero_bits(data: bytes) -> int:
115
- count = 0
116
- for b in data:
117
- if b == 0:
118
- count += 8
119
- else:
120
- count += 8 - b.bit_length()
121
- break
122
- return count
123
-
124
- while True:
125
- envelope_bytes = encode([
126
- encrypted_bytes,
127
- message_bytes,
128
- self.nonce,
129
- self.sender_bytes,
130
- timestamp_int
131
- ])
132
- envelope_hash = blake3.blake3(envelope_bytes).digest()
133
- if count_leading_zero_bits(envelope_hash) >= difficulty:
134
- self.hash = envelope_hash
135
- break
136
- self.nonce += 1
137
-
138
- def to_bytes(self):
139
- encrypted_bytes = b'\x01' if self.encrypted else b'\x00'
140
-
141
- return encode([
142
- encrypted_bytes,
143
- self.message.to_bytes(),
144
- self.nonce,
145
- self.sender.public_bytes(),
146
- int(self.timestamp.timestamp())
147
- ])
148
-
149
- @classmethod
150
- def from_bytes(cls, data: bytes):
151
- encrypted_bytes, message_bytes, nonce, sender_bytes, timestamp_int = decode(data)
152
- return cls(
153
- encrypted=(encrypted_bytes == b'\x01'),
154
- message=Message.from_bytes(message_bytes),
155
- nonce=nonce,
156
- sender=X25519PublicKey.from_public_bytes(sender_bytes),
157
- timestamp=datetime.fromtimestamp(timestamp_int, timezone.utc)
158
- )
159
-
160
65
  class Peer:
161
- def __init__(self, node_secret_key: X25519PrivateKey, peer_public_key: X25519PublicKey, address: Tuple[str, int]):
162
- self.shared_key = x25519.generate_shared_key(node_secret_key, peer_public_key)
163
- self.address = address
66
+ shared_key: bytes
67
+ timestamp: datetime
68
+ def __init__(self, my_sec_key: X25519PrivateKey, peer_pub_key: X25519PublicKey):
69
+ self.shared_key = my_sec_key.exchange(peer_pub_key)
164
70
  self.timestamp = datetime.now(timezone.utc)
165
71
 
166
72
  class Route:
@@ -419,6 +325,7 @@ class Node:
419
325
  self.peer_manager_thread.start()
420
326
 
421
327
  self.peers = Dict[X25519PublicKey, Peer]
328
+ self.addresses = Dict[Tuple[str, int], X25519PublicKey]
422
329
 
423
330
  if 'bootstrap' in config:
424
331
  for addr in config['bootstrap']:
@@ -451,9 +358,8 @@ class Node:
451
358
  # find the nearest peer route node to the hash and send an object request
452
359
  closest_peer = self._get_closest_local_peer(hash)
453
360
  if closest_peer:
454
- object_request_message = Message(topic=MessageTopic.OBJECT_REQUEST, body=hash)
455
- object_request_envelope = Envelope(message=object_request_message, sender=self.relay_public_key)
456
- self.outgoing_queue.put((object_request_envelope.to_bytes(), self.peers[closest_peer].address))
361
+ object_request_message = Message(topic=MessageTopic.OBJECT_REQUEST, content=hash)
362
+ self.outgoing_queue.put((object_request_message.to_bytes(), self.peers[closest_peer].address))
457
363
 
458
364
  # wait for upto self.storage_get_relay_timeout seconds for the object to be stored/until local_object_get returns something
459
365
  start_time = time.time()
@@ -481,30 +387,31 @@ class Node:
481
387
  while True:
482
388
  try:
483
389
  data, addr = self.incoming_queue.get()
484
- envelope = Envelope.from_bytes(data)
485
- match envelope.message.topic:
390
+ message = Message.from_bytes(data)
391
+ match message.topic:
486
392
  case MessageTopic.PING:
487
- if envelope.sender in self.peers:
488
- self.peers[envelope.sender].timestamp = datetime.now(timezone.utc)
393
+ peer_pub_key = self.addresses.get(addr)
394
+ if peer_pub_key in self.peers:
395
+ self.peers[peer_pub_key].timestamp = datetime.now(timezone.utc)
489
396
  continue
490
397
 
491
- is_validator_flag = decode(envelope.message.body)
398
+ is_validator_flag = decode(message.body)
492
399
 
493
- if envelope.sender not in self.peers:
400
+ if peer_pub_key not in self.peers:
494
401
  self._send_ping(addr)
495
402
 
496
- peer = Peer(self.relay_secret_key, envelope.sender, addr)
403
+ peer = Peer(my_sec_key=self.relay_secret_key, peer_pub_key=peer_pub_key)
497
404
  self.peers[peer.sender] = peer
498
- self.peer_route.add_peer(envelope.sender)
405
+ self.peer_route.add_peer(peer_pub_key)
499
406
  if is_validator_flag == [1]:
500
- self.validation_route.add_peer(envelope.sender)
407
+ self.validation_route.add_peer(peer_pub_key)
501
408
 
502
409
  if peer.timestamp < datetime.now(timezone.utc) - timedelta(minutes=5.0):
503
410
  self._send_ping(addr)
504
411
 
505
412
  case MessageTopic.OBJECT_REQUEST:
506
413
  try:
507
- object_request = ObjectRequest.from_bytes(envelope.message.body)
414
+ object_request = ObjectRequest.from_bytes(message.body)
508
415
 
509
416
  match object_request.type:
510
417
  # -------------- OBJECT_GET --------------
@@ -519,9 +426,8 @@ class Node:
519
426
  data=local_data,
520
427
  hash=object_hash
521
428
  )
522
- msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
523
- env = Envelope(message=msg, sender=self.relay_public_key)
524
- self.outgoing_queue.put((env.to_bytes(), addr))
429
+ obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
430
+ self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
525
431
  return # done
526
432
 
527
433
  # 2. If we know a provider, tell the requester.
@@ -534,9 +440,8 @@ class Node:
534
440
  data=provider_bytes,
535
441
  hash=object_hash
536
442
  )
537
- msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
538
- env = Envelope(message=msg, sender=self.relay_public_key)
539
- self.outgoing_queue.put((env.to_bytes(), addr))
443
+ obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
444
+ self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
540
445
  return # done
541
446
 
542
447
  # 3. Otherwise, direct the requester to a peer nearer to the hash.
@@ -555,14 +460,13 @@ class Node:
555
460
  data=peer_info,
556
461
  hash=object_hash
557
462
  )
558
- msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
559
- env = Envelope(message=msg, sender=self.relay_public_key)
560
- self.outgoing_queue.put((env.to_bytes(), addr))
463
+ obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
464
+ self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
561
465
 
562
466
  # -------------- OBJECT_PUT --------------
563
467
  case ObjectRequestType.OBJECT_PUT:
564
468
  # Ensure the hash is present / correct.
565
- obj_hash = object_request.hash or blake30.blake3(object_request.data).digest()
469
+ obj_hash = object_request.hash or blake3.blake3(object_request.data).digest()
566
470
 
567
471
  nearest = self._get_closest_local_peer(obj_hash)
568
472
  # If a strictly nearer peer exists, forward the PUT.
@@ -572,13 +476,13 @@ class Node:
572
476
  data=object_request.data,
573
477
  hash=obj_hash
574
478
  )
575
- fwd_msg = Message(topic=MessageTopic.OBJECT_REQUEST, body=fwd_req.to_bytes())
576
- fwd_env = Envelope(message=fwd_msg, sender=self.relay_public_key)
577
- self.outgoing_queue.put((fwd_env.to_bytes(), nearest[1].address))
479
+ obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, body=fwd_req.to_bytes())
480
+ self.outgoing_queue.put((obj_req_msg.to_bytes(), nearest[1].address))
578
481
  else:
579
482
  # We are closest → remember who can provide the object.
483
+ peer_pub_key = self.addresses.get(addr)
580
484
  provider_record = encode([
581
- envelope.sender.public_bytes(),
485
+ peer_pub_key.public_bytes(),
582
486
  encode_ip_address(*addr)
583
487
  ])
584
488
  if not hasattr(self, "storage_index") or not isinstance(self.storage_index, dict):
@@ -590,13 +494,13 @@ class Node:
590
494
 
591
495
  case MessageTopic.OBJECT_RESPONSE:
592
496
  try:
593
- object_response = ObjectResponse.from_bytes(envelope.message.body)
497
+ object_response = ObjectResponse.from_bytes(message.body)
594
498
  if object_response.hash not in self.object_request_queue:
595
499
  continue
596
500
 
597
501
  match object_response.type:
598
502
  case ObjectResponseType.OBJECT_FOUND:
599
- if object_response.hash != blake30.blake3(object_response.data).digest():
503
+ if object_response.hash != blake3.blake3(object_response.data).digest():
600
504
  continue
601
505
  self.object_request_queue.remove(object_response.hash)
602
506
  self._local_object_put(object_response.hash, object_response.data)
@@ -604,9 +508,8 @@ class Node:
604
508
  case ObjectResponseType.OBJECT_PROVIDER:
605
509
  _provider_public_key, provider_address = decode(object_response.data)
606
510
  provider_ip, provider_port = decode_ip_address(provider_address)
607
- object_request_message = Message(topic=MessageTopic.OBJECT_REQUEST, body=object_hash)
608
- object_request_envelope = Envelope(message=object_request_message, sender=self.relay_public_key)
609
- self.outgoing_queue.put((object_request_envelope.to_bytes(), (provider_ip, provider_port)))
511
+ obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, body=object_hash)
512
+ self.outgoing_queue.put((obj_req_msg.to_bytes(), (provider_ip, provider_port)))
610
513
 
611
514
  case ObjectResponseType.OBJECT_NEAREST_PEER:
612
515
  # -- decode the peer info sent back
@@ -630,23 +533,10 @@ class Node:
630
533
  if self._is_closer_than_local_peers(
631
534
  object_response.hash, nearest_peer_public_key
632
535
  ):
633
- nearest_peer_ip, nearest_peer_port = decode_ip_address(
634
- nearest_peer_address
635
- )
636
- object_request_message = Message(
637
- topic=MessageTopic.OBJECT_REQUEST,
638
- body=object_response.hash,
639
- )
640
- object_request_envelope = Envelope(
641
- message=object_request_message,
642
- sender=self.relay_public_key,
643
- )
644
- self.outgoing_queue.put(
645
- (
646
- object_request_envelope.to_bytes(),
647
- (nearest_peer_ip, nearest_peer_port),
648
- )
649
- )
536
+ nearest_peer_ip, nearest_peer_port = decode_ip_address(nearest_peer_address)
537
+ obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, content=object_response.hash)
538
+ self.outgoing_queue.put((obj_req_msg.to_bytes(), (nearest_peer_ip, nearest_peer_port),)
539
+ )
650
540
 
651
541
 
652
542
  except Exception as e:
@@ -678,9 +568,8 @@ class Node:
678
568
 
679
569
  def _send_ping(self, addr: Tuple[str, int]):
680
570
  is_validator_flag = encode([1] if self.validation_secret_key else [0])
681
- ping_message = Message(topic=MessageTopic.PING, body=is_validator_flag)
682
- ping_envelope = Envelope(message=ping_message, sender=self.relay_public_key)
683
- self.outgoing_queue.put((ping_envelope.to_bytes(), addr))
571
+ ping_message = Message(topic=MessageTopic.PING, content=is_validator_flag)
572
+ self.outgoing_queue.put((ping_message.to_bytes(), addr))
684
573
 
685
574
  def _get_closest_local_peer(self, hash: bytes) -> Optional[Tuple[X25519PublicKey, Peer]]:
686
575
  # Find the globally closest peer using XOR distance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.25
3
+ Version: 0.2.27
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
  astreum/__init__.py,sha256=y2Ok3EY_FstcmlVASr80lGR_0w-dH-SXDCCQFmL6uwA,28
2
2
  astreum/format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
3
- astreum/node.py,sha256=dPloCXuDyIn3-KDqxlgl3jxsonJlFMLi_quwJRsoLC8,46259
3
+ astreum/node.py,sha256=DZyFQLQcdcNw-Vl3JrJzNbDKAiEqSbNQhyOLeGWGXz4,41967
4
4
  astreum/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  astreum/crypto/ed25519.py,sha256=FRnvlN0kZlxn4j-sJKl-C9tqiz_0z4LZyXLj3KIj1TQ,1760
6
6
  astreum/crypto/quadratic_form.py,sha256=pJgbORey2NTWbQNhdyvrjy_6yjORudQ67jBz2ScHptg,4037
@@ -12,12 +12,13 @@ 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=4slG6z6pZUyxQUu2on4c8nT24ztzJuQBZRje9KyRTXE,13840
16
- astreum/models/merkle.py,sha256=merV3rx2iRfzvglV6gNusrJf7OMbcVV854T-DUWCC64,6733
15
+ astreum/models/block.py,sha256=-5j7uO0woVtNi0h52__e7AxpDQSVhzKUhr6Qc-2xZsE,17870
16
+ astreum/models/merkle.py,sha256=lvWJa9nmrBL0n_2h_uNqpB_9a5s5Hn1FceRLx0IZIVQ,6778
17
+ astreum/models/message.py,sha256=vv8yx-ndVYjCmPM4gXRVMToCTlKY_mflPu0uKsb9iiE,2117
17
18
  astreum/models/patricia.py,sha256=ohmXrcaz7Ae561tyC4u4iPOkQPkKr8N0IWJek4upFIg,13392
18
- astreum/models/transaction.py,sha256=yBarvRK2ybMAHHEQPZpubGO7gms4U9k093xQGNQHQ4Q,3043
19
- astreum-0.2.25.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
20
- astreum-0.2.25.dist-info/METADATA,sha256=BqaRK8hea6p3iMUkin5gqzivjdOz16_alamM9jFkteQ,5478
21
- astreum-0.2.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- astreum-0.2.25.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
23
- astreum-0.2.25.dist-info/RECORD,,
19
+ astreum/models/transaction.py,sha256=MkLL5YX18kIf9-O4LBaZ4eWjkXDAaYIrDcDehbDZoqg,3038
20
+ astreum-0.2.27.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
21
+ astreum-0.2.27.dist-info/METADATA,sha256=nMaxqfSmJtxic-_mIXivDbg2WszmnGkDnniiX1hqDH4,5478
22
+ astreum-0.2.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ astreum-0.2.27.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
24
+ astreum-0.2.27.dist-info/RECORD,,