astreum 0.1.13__py3-none-any.whl → 0.1.15__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.

@@ -4,6 +4,8 @@ Relay module for handling network communication in the Astreum node.
4
4
 
5
5
  import socket
6
6
  import threading
7
+ import random
8
+ import time
7
9
  from queue import Queue
8
10
  from typing import Tuple, Callable, Dict, Set, Optional, List
9
11
  from .message import Message, Topic
@@ -43,14 +45,16 @@ class Relay:
43
45
  # Save private key bytes for config persistence
44
46
  self.private_key_bytes = self.private_key.private_bytes_raw()
45
47
 
46
- # Routes that this node participates in (0 = peer route, 1 = validation route)
48
+ # Routes that this node participates in
49
+ # 0 = peer route, 1 = validation route
50
+ # All routes are tracked by default, but we only join some
47
51
  self.routes: List[int] = []
52
+ self.tracked_routes: List[int] = [0, 1] # Track all routes
48
53
 
49
- # Peer route is always enabled
54
+ # Always join peer route
50
55
  self.routes.append(0) # Peer route
51
56
 
52
- # Check if the node should join validation route
53
- # This is now controlled by the parent Node class based on validation_private_key
57
+ # Check if this node should join validation route
54
58
  if config.get('validation_route', False):
55
59
  self.routes.append(1) # Validation route
56
60
 
@@ -102,24 +106,53 @@ class Relay:
102
106
  def is_in_validation_route(self) -> bool:
103
107
  """Check if this node is part of the validation route."""
104
108
  return 1 in self.routes
109
+
110
+ def is_tracking_route(self, route_type: int) -> bool:
111
+ """Check if this node is tracking a specific route."""
112
+ return route_type in self.tracked_routes
105
113
 
106
- def add_peer_to_route(self, peer: Peer, route_types: List[int]):
114
+ def get_random_peers_from_route(self, route_type: int, count: int = 3) -> List[Peer]:
107
115
  """
108
- Add a peer to specified routes.
116
+ Get a list of random peers from different buckets in the specified route.
109
117
 
110
118
  Args:
111
- peer (Peer): The peer to add
112
- route_types (List[int]): List of route types to add the peer to (0 = peer, 1 = validation)
119
+ route_type (int): Route type (0 for peer, 1 for validation)
120
+ count (int): Number of random peers to select (one from each bucket)
121
+
122
+ Returns:
123
+ List[Peer]: List of randomly selected peers from different buckets
113
124
  """
114
- for route_type in route_types:
115
- if route_type == 0: # Peer route
116
- # Add to top of bucket, eject last if at capacity
117
- self.peer_route_bucket.add(peer, to_front=True)
118
- elif route_type == 1: # Validation route
119
- # Add to top of bucket, eject last if at capacity
120
- self.validation_route_bucket.add(peer, to_front=True)
125
+ result = []
126
+ route_id = self._get_route_id(route_type)
127
+
128
+ # Get all buckets that have peers for this route
129
+ buckets_with_peers = []
130
+ for i, bucket in enumerate(self.routing_table):
131
+ # For each bucket, collect peers that are in this route
132
+ route_peers_in_bucket = [peer for peer in bucket.values()
133
+ if peer.routes and route_id in peer.routes]
134
+ if route_peers_in_bucket:
135
+ buckets_with_peers.append((i, route_peers_in_bucket))
136
+
137
+ # If we don't have any buckets with peers, return empty list
138
+ if not buckets_with_peers:
139
+ return []
140
+
141
+ # If we have fewer buckets than requested count, adjust count
142
+ sample_count = min(count, len(buckets_with_peers))
143
+
144
+ # Sample random buckets
145
+ selected_buckets = random.sample(buckets_with_peers, sample_count)
146
+
147
+ # For each selected bucket, pick one random peer
148
+ for bucket_idx, peers in selected_buckets:
149
+ # Select one random peer from this bucket
150
+ selected_peer = random.choice(peers)
151
+ result.append(selected_peer)
121
152
 
122
- def get_route_peers(self, route_type: int) -> List[Peer]:
153
+ return result
154
+
155
+ def get_peers_in_route(self, route_type: int) -> List[Peer]:
123
156
  """
124
157
  Get all peers in a specific route.
125
158
 
@@ -135,6 +168,22 @@ class Relay:
135
168
  return self.validation_route_bucket.get_peers()
136
169
  return []
137
170
 
171
+ def add_peer_to_route(self, peer: Peer, route_types: List[int]):
172
+ """
173
+ Add a peer to specified routes.
174
+
175
+ Args:
176
+ peer (Peer): The peer to add
177
+ route_types (List[int]): List of route types to add the peer to (0 = peer, 1 = validation)
178
+ """
179
+ for route_type in route_types:
180
+ if route_type == 0: # Peer route
181
+ # Add to top of bucket, eject last if at capacity
182
+ self.peer_route_bucket.add(peer, to_front=True)
183
+ elif route_type == 1: # Validation route
184
+ # Add to top of bucket, eject last if at capacity
185
+ self.validation_route_bucket.add(peer, to_front=True)
186
+
138
187
  def register_message_handler(self, topic: Topic, handler_func):
139
188
  """Register a handler function for a specific message topic."""
140
189
  self.message_handlers[topic] = handler_func
@@ -197,26 +246,19 @@ class Relay:
197
246
  """Handle an incoming message."""
198
247
  envelope = Envelope.from_bytes(data)
199
248
  if envelope and envelope.message.topic in self.message_handlers:
200
- # Check if this is a transaction or block message that requires validation route
201
- if envelope.message.topic in (Topic.TRANSACTION, Topic.BLOCK):
202
- # Only process if we're part of the validation route
249
+ # For transaction messages, only process if we're in validation route
250
+ if envelope.message.topic == Topic.TRANSACTION:
203
251
  if self.is_in_validation_route():
204
252
  self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
205
- elif envelope.message.topic == Topic.LATEST_BLOCK:
206
- # For latest_block, we only process if we're in the validation route
253
+ # For block messages, only process if we're in validation route
254
+ elif envelope.message.topic == Topic.BLOCK:
207
255
  if self.is_in_validation_route():
208
256
  self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
209
- elif envelope.message.topic in (Topic.LATEST_BLOCK_REQUEST, Topic.GET_BLOCKS):
210
- # Allow all nodes to request blocks for syncing
211
- self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
212
- elif envelope.message.topic == Topic.OBJECT_REQUEST:
213
- # Handle object request
214
- self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
215
- elif envelope.message.topic == Topic.OBJECT_RESPONSE:
216
- # Handle object response
257
+ # Latest block and latest block requests can be handled by any node tracking the routes
258
+ elif envelope.message.topic in (Topic.LATEST_BLOCK, Topic.LATEST_BLOCK_REQUEST):
217
259
  self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
260
+ # For other message types, always process
218
261
  else:
219
- # For other message types, always process
220
262
  self.message_handlers[envelope.message.topic](envelope.message.body, addr, envelope)
221
263
 
222
264
  def send(self, data: bytes, addr: Tuple[str, int]):
@@ -31,11 +31,11 @@ determined by the difficulty parameter. The nonce is adjusted until this require
31
31
  import struct
32
32
  import time
33
33
  import os
34
- import hashlib
35
34
  from dataclasses import dataclass
36
35
  from typing import Optional, Tuple, List
37
36
  from .message import Message, Topic
38
37
  from astreum.utils.bytes_format import encode, decode
38
+ from ..utils import hash_data
39
39
 
40
40
  @dataclass
41
41
  class Envelope:
@@ -153,13 +153,13 @@ class Envelope:
153
153
  bytes: The Merkle root hash
154
154
  """
155
155
  if not leaves:
156
- return hashlib.sha256(b'').digest()
156
+ return hash_data(b'')
157
157
 
158
158
  if len(leaves) == 1:
159
- return hashlib.sha256(leaves[0]).digest()
159
+ return hash_data(leaves[0])
160
160
 
161
161
  # Hash all leaf nodes
162
- hashed_leaves = [hashlib.sha256(leaf).digest() for leaf in leaves]
162
+ hashed_leaves = [hash_data(leaf) for leaf in leaves]
163
163
 
164
164
  # Build the Merkle tree
165
165
  while len(hashed_leaves) > 1:
@@ -171,7 +171,7 @@ class Envelope:
171
171
  next_level = []
172
172
  for i in range(0, len(hashed_leaves), 2):
173
173
  combined = hashed_leaves[i] + hashed_leaves[i+1]
174
- next_level.append(hashlib.sha256(combined).digest())
174
+ next_level.append(hash_data(combined))
175
175
 
176
176
  hashed_leaves = next_level
177
177
 
@@ -0,0 +1,13 @@
1
+ """
2
+ Storage utilities for the Astreum node.
3
+ """
4
+
5
+ from .merkle import MerkleTree, MerkleProof, MerkleNode
6
+ from .merkle import find_first, find_all, map, binary_search
7
+ from .storage import Storage
8
+
9
+ __all__ = [
10
+ "MerkleTree", "MerkleProof", "MerkleNode",
11
+ "find_first", "find_all", "map", "binary_search",
12
+ "Storage"
13
+ ]