astreum 0.1.18__py3-none-any.whl → 0.1.20__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/node/__init__.py CHANGED
@@ -1,475 +1,447 @@
1
- # import time
2
- # import threading
3
- # from typing import List
4
- # from cryptography.hazmat.primitives.asymmetric import ed25519
5
- # from cryptography.hazmat.primitives import serialization
1
+ import time
2
+ import threading
3
+ from typing import List
4
+ from cryptography.hazmat.primitives.asymmetric import ed25519
5
+ from cryptography.hazmat.primitives import serialization
6
6
 
7
- # from .relay import Relay, Topic
8
- # from .machine import AstreumMachine
9
- # from .utils import hash_data
10
- # from .validation.block import Block
11
- # from .storage import Storage
7
+ from .relay import Relay, Topic
8
+ from ..machine import AstreumMachine
9
+ from .utils import hash_data
10
+ from .validation.block import Block
11
+ from .storage.storage import Storage
12
12
 
13
- # # Import our validation components using the new functional approach
14
- # from .validation import (
15
- # validate_block,
16
- # create_block,
17
- # create_genesis_block,
18
- # compute_vdf,
19
- # verify_vdf,
20
- # select_validator,
21
- # select_validator_for_slot,
22
- # get_validator_stake,
23
- # is_validator,
24
- # VALIDATION_ADDRESS,
25
- # BURN_ADDRESS,
26
- # MIN_STAKE_AMOUNT,
27
- # SLOT_DURATION,
28
- # VDF_DIFFICULTY
29
- # )
30
- # from .validation.state import (
31
- # add_block_to_state,
32
- # validate_and_apply_block,
33
- # create_account_state,
34
- # get_validator_for_slot,
35
- # select_best_chain,
36
- # compare_chains,
37
- # get_validator_set
38
- # )
39
- # from .validation.adapter import BlockAdapter, TransactionAdapter
40
-
41
- # class Node:
42
- # def __init__(self, config: dict):
43
- # # Ensure config is a dictionary, but allow it to be None
44
- # self.config = config if config is not None else {}
45
-
46
- # # Handle validation key if provided
47
- # self.validation_private_key = None
48
- # self.validation_public_key = None
49
- # self.is_validator = False
50
-
51
- # # Extract validation private key from config
52
- # if 'validation_private_key' in self.config:
53
- # try:
54
- # key_bytes = bytes.fromhex(self.config['validation_private_key'])
55
- # self.validation_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(key_bytes)
56
- # self.validation_public_key = self.validation_private_key.public_key()
57
- # self.is_validator = True
13
+ class Node:
14
+ def __init__(self, config: dict):
15
+ # Ensure config is a dictionary, but allow it to be None
16
+ self.config = config if config is not None else {}
17
+
18
+ # Handle validation key if provided
19
+ self.validation_private_key = None
20
+ self.validation_public_key = None
21
+ self.is_validator = False
22
+
23
+ # Extract validation private key from config
24
+ if 'validation_private_key' in self.config:
25
+ try:
26
+ key_bytes = bytes.fromhex(self.config['validation_private_key'])
27
+ self.validation_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(key_bytes)
28
+ self.validation_public_key = self.validation_private_key.public_key()
29
+ self.is_validator = True
58
30
 
59
- # # Set validation_route to True in config so relay will join validation route
60
- # self.config['validation_route'] = True
61
- # print(f"Node is configured as a validator with validation key")
62
- # except Exception as e:
63
- # print(f"Error loading validation private key: {e}")
64
-
65
- # # Initialize relay with our config
66
- # self.relay = Relay(self.config)
67
-
68
- # # Get the node_id from relay
69
- # self.node_id = self.relay.node_id
70
-
71
- # # Initialize storage
72
- # self.storage = Storage(self.config)
73
- # self.storage.node = self # Set the storage node reference to self
74
-
75
- # # Initialize blockchain state
76
- # self.blockchain = create_account_state(self.config)
77
-
78
- # # Store our validator info if we're a validator
79
- # if self.is_validator and self.validation_public_key:
80
- # self.validator_address = self.validation_public_key.public_bytes(
81
- # encoding=serialization.Encoding.Raw,
82
- # format=serialization.PublicFormat.Raw
83
- # )
84
- # self.validator_private_bytes = self.validation_private_key.private_bytes(
85
- # encoding=serialization.Encoding.Raw,
86
- # format=serialization.PrivateFormat.Raw,
87
- # encryption_algorithm=serialization.NoEncryption()
88
- # )
89
- # print(f"Registered validator with address: {self.validator_address.hex()}")
90
- # else:
91
- # self.validator_address = None
92
- # self.validator_private_bytes = None
93
-
94
- # # Latest block of the chain this node is following
95
- # self.latest_block = None
96
- # self.followed_chain_id = self.config.get('followed_chain_id', None)
97
-
98
- # # Initialize machine
99
- # self.machine = AstreumMachine(node=self)
100
-
101
- # # Register message handlers
102
- # self.relay.message_handlers[Topic.PEER_ROUTE] = self._handle_peer_route
103
- # self.relay.message_handlers[Topic.PING] = self._handle_ping
104
- # self.relay.message_handlers[Topic.PONG] = self._handle_pong
105
- # self.relay.message_handlers[Topic.OBJECT_REQUEST] = self._handle_object_request
106
- # self.relay.message_handlers[Topic.OBJECT_RESPONSE] = self._handle_object_response
107
- # self.relay.message_handlers[Topic.ROUTE_REQUEST] = self._handle_route_request
108
- # self.relay.message_handlers[Topic.ROUTE] = self._handle_route
109
- # self.relay.message_handlers[Topic.LATEST_BLOCK_REQUEST] = self._handle_latest_block_request
110
- # self.relay.message_handlers[Topic.LATEST_BLOCK] = self._handle_latest_block
111
- # self.relay.message_handlers[Topic.TRANSACTION] = self._handle_transaction
112
- # self.relay.message_handlers[Topic.BLOCK_REQUEST] = self._handle_block_request
113
- # self.relay.message_handlers[Topic.BLOCK_RESPONSE] = self._handle_block_response
114
-
115
- # # Initialize latest block from storage if available
116
- # self._initialize_latest_block()
117
-
118
- # # Candidate chains that might be adopted
119
- # self.candidate_chains = {} # chain_id -> {'latest_block': block, 'timestamp': time.time()}
120
- # self.pending_blocks = {} # block_hash -> {'block': block, 'timestamp': time.time()}
121
-
122
- # # Threads for validation and chain monitoring
123
- # self.running = False
124
- # self.main_chain_validation_thread = None
125
- # self.candidate_chain_validation_thread = None
126
-
127
- # # Pending transactions for a block
128
- # self.pending_transactions = {} # tx_hash -> {'transaction': tx, 'timestamp': time.time()}
129
-
130
- # # Last block production attempt time
131
- # self.last_block_attempt_time = 0
31
+ # Set validation_route to True in config so relay will join validation route
32
+ self.config['validation_route'] = True
33
+ print(f"Node is configured as a validator with validation key")
34
+ except Exception as e:
35
+ print(f"Error loading validation private key: {e}")
36
+
37
+ # Initialize relay with our config
38
+ self.relay = Relay(self.config)
39
+
40
+ # Get the node_id from relay
41
+ self.node_id = self.relay.node_id
42
+
43
+ # Initialize storage
44
+ self.storage = Storage(self.config)
45
+ self.storage.node = self # Set the storage node reference to self
46
+
47
+ # Initialize blockchain state
48
+ self.blockchain = create_account_state(self.config)
49
+
50
+ # Store our validator info if we're a validator
51
+ if self.is_validator and self.validation_public_key:
52
+ self.validator_address = self.validation_public_key.public_bytes(
53
+ encoding=serialization.Encoding.Raw,
54
+ format=serialization.PublicFormat.Raw
55
+ )
56
+ self.validator_private_bytes = self.validation_private_key.private_bytes(
57
+ encoding=serialization.Encoding.Raw,
58
+ format=serialization.PrivateFormat.Raw,
59
+ encryption_algorithm=serialization.NoEncryption()
60
+ )
61
+ print(f"Registered validator with address: {self.validator_address.hex()}")
62
+ else:
63
+ self.validator_address = None
64
+ self.validator_private_bytes = None
65
+
66
+ # Latest block of the chain this node is following
67
+ self.latest_block = None
68
+ self.followed_chain_id = self.config.get('followed_chain_id', None)
69
+
70
+ # Initialize machine
71
+ self.machine = AstreumMachine(node=self)
72
+
73
+ # Register message handlers
74
+ self.relay.message_handlers[Topic.PEER_ROUTE] = self._handle_peer_route
75
+ self.relay.message_handlers[Topic.PING] = self._handle_ping
76
+ self.relay.message_handlers[Topic.PONG] = self._handle_pong
77
+ self.relay.message_handlers[Topic.OBJECT_REQUEST] = self._handle_object_request
78
+ self.relay.message_handlers[Topic.OBJECT_RESPONSE] = self._handle_object_response
79
+ self.relay.message_handlers[Topic.ROUTE_REQUEST] = self._handle_route_request
80
+ self.relay.message_handlers[Topic.ROUTE] = self._handle_route
81
+ self.relay.message_handlers[Topic.LATEST_BLOCK_REQUEST] = self._handle_latest_block_request
82
+ self.relay.message_handlers[Topic.LATEST_BLOCK] = self._handle_latest_block
83
+ self.relay.message_handlers[Topic.TRANSACTION] = self._handle_transaction
84
+ self.relay.message_handlers[Topic.BLOCK_REQUEST] = self._handle_block_request
85
+ self.relay.message_handlers[Topic.BLOCK_RESPONSE] = self._handle_block_response
86
+
87
+ # Initialize latest block from storage if available
88
+ self._initialize_latest_block()
89
+
90
+ # Candidate chains that might be adopted
91
+ self.candidate_chains = {} # chain_id -> {'latest_block': block, 'timestamp': time.time()}
92
+ self.pending_blocks = {} # block_hash -> {'block': block, 'timestamp': time.time()}
93
+
94
+ # Threads for validation and chain monitoring
95
+ self.running = False
96
+ self.main_chain_validation_thread = None
97
+ self.candidate_chain_validation_thread = None
98
+
99
+ # Pending transactions for a block
100
+ self.pending_transactions = {} # tx_hash -> {'transaction': tx, 'timestamp': time.time()}
101
+
102
+ # Last block production attempt time
103
+ self.last_block_attempt_time = 0
132
104
 
133
- # def start(self):
134
- # """Start the node."""
135
- # self.running = True
136
-
137
- # # Start relay
138
- # self.relay.start()
139
-
140
- # # Start chain monitoring thread
141
- # self.main_chain_validation_thread = threading.Thread(
142
- # target=self._main_chain_validation_loop,
143
- # name="MainChainValidation"
144
- # )
145
- # self.main_chain_validation_thread.daemon = True
146
- # self.main_chain_validation_thread.start()
147
-
148
- # self.candidate_chain_validation_thread = threading.Thread(
149
- # target=self._candidate_chain_validation_loop,
150
- # name="CandidateChainValidation"
151
- # )
152
- # self.candidate_chain_validation_thread.daemon = True
153
- # self.candidate_chain_validation_thread.start()
154
-
155
- # # Set up recurring block query tasks
156
- # main_query_thread = threading.Thread(
157
- # target=self._block_query_loop,
158
- # args=('main',),
159
- # daemon=True
160
- # )
161
- # main_query_thread.start()
162
-
163
- # validation_query_thread = threading.Thread(
164
- # target=self._block_query_loop,
165
- # args=('validation',),
166
- # daemon=True
167
- # )
168
- # validation_query_thread.start()
169
-
170
- # print(f"Node started with ID {self.node_id.hex()}")
105
+ def start(self):
106
+ """Start the node."""
107
+ self.running = True
108
+
109
+ # Start relay
110
+ self.relay.start()
111
+
112
+ # Start chain monitoring thread
113
+ self.main_chain_validation_thread = threading.Thread(
114
+ target=self._main_chain_validation_loop,
115
+ name="MainChainValidation"
116
+ )
117
+ self.main_chain_validation_thread.daemon = True
118
+ self.main_chain_validation_thread.start()
119
+
120
+ self.candidate_chain_validation_thread = threading.Thread(
121
+ target=self._candidate_chain_validation_loop,
122
+ name="CandidateChainValidation"
123
+ )
124
+ self.candidate_chain_validation_thread.daemon = True
125
+ self.candidate_chain_validation_thread.start()
126
+
127
+ # Set up recurring block query tasks
128
+ main_query_thread = threading.Thread(
129
+ target=self._block_query_loop,
130
+ args=('main',),
131
+ daemon=True
132
+ )
133
+ main_query_thread.start()
134
+
135
+ validation_query_thread = threading.Thread(
136
+ target=self._block_query_loop,
137
+ args=('validation',),
138
+ daemon=True
139
+ )
140
+ validation_query_thread.start()
141
+
142
+ print(f"Node started with ID {self.node_id.hex()}")
171
143
 
172
- # def stop(self):
173
- # """Stop the node and all its services."""
174
- # self.running = False
144
+ def stop(self):
145
+ """Stop the node and all its services."""
146
+ self.running = False
175
147
 
176
- # # Stop all threads
177
- # if self.main_chain_validation_thread and self.main_chain_validation_thread.is_alive():
178
- # self.main_chain_validation_thread.join(timeout=1.0)
148
+ # Stop all threads
149
+ if self.main_chain_validation_thread and self.main_chain_validation_thread.is_alive():
150
+ self.main_chain_validation_thread.join(timeout=1.0)
179
151
 
180
- # if self.candidate_chain_validation_thread and self.candidate_chain_validation_thread.is_alive():
181
- # self.candidate_chain_validation_thread.join(timeout=1.0)
152
+ if self.candidate_chain_validation_thread and self.candidate_chain_validation_thread.is_alive():
153
+ self.candidate_chain_validation_thread.join(timeout=1.0)
182
154
 
183
- # # Stop relay last
184
- # if self.relay:
185
- # self.relay.stop()
155
+ # Stop relay last
156
+ if self.relay:
157
+ self.relay.stop()
186
158
 
187
- # print("Node stopped")
159
+ print("Node stopped")
188
160
 
189
- # def _main_chain_validation_loop(self):
190
- # """
191
- # Main validation loop for the primary blockchain.
192
- # This thread prioritizes validating blocks on the main chain we're following.
193
- # """
194
- # while self.running:
195
- # try:
196
- # # Update latest block if we don't have one yet
197
- # if not self.latest_block and hasattr(self.blockchain, 'get_latest_block'):
198
- # self.latest_block = self.blockchain.get_latest_block()
161
+ def _main_chain_validation_loop(self):
162
+ """
163
+ Main validation loop for the primary blockchain.
164
+ This thread prioritizes validating blocks on the main chain we're following.
165
+ """
166
+ while self.running:
167
+ try:
168
+ # Update latest block if we don't have one yet
169
+ if not self.latest_block and hasattr(self.blockchain, 'get_latest_block'):
170
+ self.latest_block = self.blockchain.get_latest_block()
199
171
 
200
- # # Process any blocks that extend our main chain immediately
201
- # self._process_main_chain_blocks()
172
+ # Process any blocks that extend our main chain immediately
173
+ self._process_main_chain_blocks()
202
174
 
203
- # # Attempt block production if we are a validator
204
- # if self.is_validator and self.validator_address:
205
- # self._attempt_block_production()
175
+ # Attempt block production if we are a validator
176
+ if self.is_validator and self.validator_address:
177
+ self._attempt_block_production()
206
178
 
207
- # # Cleanup old items
208
- # self._prune_pending_items()
179
+ # Cleanup old items
180
+ self._prune_pending_items()
209
181
 
210
- # # Sleep to prevent high CPU usage
211
- # time.sleep(0.1) # Short sleep for main chain validation
212
- # except Exception as e:
213
- # print(f"Error in main chain validation loop: {e}")
214
- # time.sleep(1) # Longer sleep on error
182
+ # Sleep to prevent high CPU usage
183
+ time.sleep(0.1) # Short sleep for main chain validation
184
+ except Exception as e:
185
+ print(f"Error in main chain validation loop: {e}")
186
+ time.sleep(1) # Longer sleep on error
215
187
 
216
- # def _candidate_chain_validation_loop(self):
217
- # """
218
- # Validation loop for candidate chains (potential forks).
219
- # This thread handles validation of blocks from alternate chains
220
- # without slowing down the main chain processing.
221
- # """
222
- # while self.running:
223
- # try:
224
- # # Process candidate chains
225
- # self._evaluate_candidate_chains()
188
+ def _candidate_chain_validation_loop(self):
189
+ """
190
+ Validation loop for candidate chains (potential forks).
191
+ This thread handles validation of blocks from alternate chains
192
+ without slowing down the main chain processing.
193
+ """
194
+ while self.running:
195
+ try:
196
+ # Process candidate chains
197
+ self._evaluate_candidate_chains()
226
198
 
227
- # # Prune old candidate chains
228
- # self._prune_candidate_chains()
199
+ # Prune old candidate chains
200
+ self._prune_candidate_chains()
229
201
 
230
- # # Sleep longer for candidate chain validation (lower priority)
231
- # time.sleep(1) # Longer sleep for candidate chain validation
232
- # except Exception as e:
233
- # print(f"Error in candidate chain validation loop: {e}")
234
- # time.sleep(2) # Even longer sleep on error
202
+ # Sleep longer for candidate chain validation (lower priority)
203
+ time.sleep(1) # Longer sleep for candidate chain validation
204
+ except Exception as e:
205
+ print(f"Error in candidate chain validation loop: {e}")
206
+ time.sleep(2) # Even longer sleep on error
235
207
 
236
- # def _prune_pending_items(self):
237
- # """Remove old pending blocks and transactions."""
238
- # current_time = time.time()
239
-
240
- # # Prune old pending blocks (older than 1 hour)
241
- # blocks_to_remove = [
242
- # block_hash for block_hash, data in self.pending_blocks.items()
243
- # if current_time - data['timestamp'] > 3600 # 1 hour
244
- # ]
245
- # for block_hash in blocks_to_remove:
246
- # del self.pending_blocks[block_hash]
208
+ def _prune_pending_items(self):
209
+ """Remove old pending blocks and transactions."""
210
+ current_time = time.time()
211
+
212
+ # Prune old pending blocks (older than 1 hour)
213
+ blocks_to_remove = [
214
+ block_hash for block_hash, data in self.pending_blocks.items()
215
+ if current_time - data['timestamp'] > 3600 # 1 hour
216
+ ]
217
+ for block_hash in blocks_to_remove:
218
+ del self.pending_blocks[block_hash]
247
219
 
248
- # # Prune old pending transactions (older than 30 minutes)
249
- # txs_to_remove = [
250
- # tx_hash for tx_hash, data in self.pending_transactions.items()
251
- # if current_time - data['timestamp'] > 1800 # 30 minutes
252
- # ]
253
- # for tx_hash in txs_to_remove:
254
- # del self.pending_transactions[tx_hash]
220
+ # Prune old pending transactions (older than 30 minutes)
221
+ txs_to_remove = [
222
+ tx_hash for tx_hash, data in self.pending_transactions.items()
223
+ if current_time - data['timestamp'] > 1800 # 30 minutes
224
+ ]
225
+ for tx_hash in txs_to_remove:
226
+ del self.pending_transactions[tx_hash]
255
227
 
256
- # def _process_main_chain_blocks(self):
257
- # """
258
- # Process blocks that extend our current main chain.
259
- # Prioritizes blocks that build on our latest block.
260
- # """
261
- # # Skip if we don't have a latest block yet
262
- # if not self.latest_block:
263
- # return
228
+ def _process_main_chain_blocks(self):
229
+ """
230
+ Process blocks that extend our current main chain.
231
+ Prioritizes blocks that build on our latest block.
232
+ """
233
+ # Skip if we don't have a latest block yet
234
+ if not self.latest_block:
235
+ return
264
236
 
265
- # # Get the hash of our latest block
266
- # latest_hash = self.latest_block.get_hash()
237
+ # Get the hash of our latest block
238
+ latest_hash = self.latest_block.get_hash()
267
239
 
268
- # # Find any pending blocks that build on our latest block
269
- # main_chain_blocks = []
270
- # for block_hash, data in list(self.pending_blocks.items()):
271
- # block = data['block']
240
+ # Find any pending blocks that build on our latest block
241
+ main_chain_blocks = []
242
+ for block_hash, data in list(self.pending_blocks.items()):
243
+ block = data['block']
272
244
 
273
- # # Check if this block extends our latest block
274
- # if block.previous == latest_hash:
275
- # main_chain_blocks.append(block)
245
+ # Check if this block extends our latest block
246
+ if block.previous == latest_hash:
247
+ main_chain_blocks.append(block)
276
248
 
277
- # # Process found blocks
278
- # for block in main_chain_blocks:
279
- # self._validate_and_process_main_chain_block(block)
249
+ # Process found blocks
250
+ for block in main_chain_blocks:
251
+ self._validate_and_process_main_chain_block(block)
280
252
 
281
- # def _validate_and_process_main_chain_block(self, block: Block):
282
- # """
283
- # Validate and process a block that extends our main chain.
284
-
285
- # Args:
286
- # block: Block to validate and process
287
- # """
288
- # try:
289
- # # Validate block
290
- # is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
253
+ def _validate_and_process_main_chain_block(self, block: Block):
254
+ """
255
+ Validate and process a block that extends our main chain.
256
+
257
+ Args:
258
+ block: Block to validate and process
259
+ """
260
+ try:
261
+ # Validate block
262
+ is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
291
263
 
292
- # if is_valid:
293
- # # Apply block to our state
294
- # success = validate_and_apply_block(self.blockchain, block)
295
- # if success:
296
- # print(f"Applied valid block {block.number} to blockchain state")
297
- # self._update_latest_block(block)
298
- # blocks_to_remove = [block.get_hash()]
299
- # for block_hash in blocks_to_remove:
300
- # if block_hash in self.pending_blocks:
301
- # del self.pending_blocks[block_hash]
302
- # print(f"Added block {block.number} to blockchain")
303
- # return True
304
- # except Exception as e:
305
- # print(f"Error validating main chain block {block.number}: {e}")
264
+ if is_valid:
265
+ # Apply block to our state
266
+ success = validate_and_apply_block(self.blockchain, block)
267
+ if success:
268
+ print(f"Applied valid block {block.number} to blockchain state")
269
+ self._update_latest_block(block)
270
+ blocks_to_remove = [block.get_hash()]
271
+ for block_hash in blocks_to_remove:
272
+ if block_hash in self.pending_blocks:
273
+ del self.pending_blocks[block_hash]
274
+ print(f"Added block {block.number} to blockchain")
275
+ return True
276
+ except Exception as e:
277
+ print(f"Error validating main chain block {block.number}: {e}")
306
278
 
307
- # return False
279
+ return False
308
280
 
309
- # def _evaluate_candidate_chains(self):
310
- # """
311
- # Evaluate candidate chains to determine if any should become our main chain.
312
- # This will validate pending blocks and look for chains with higher cumulative difficulty.
313
- # """
314
- # # Skip if no candidate chains
315
- # if not self.candidate_chains:
316
- # return
281
+ def _evaluate_candidate_chains(self):
282
+ """
283
+ Evaluate candidate chains to determine if any should become our main chain.
284
+ This will validate pending blocks and look for chains with higher cumulative difficulty.
285
+ """
286
+ # Skip if no candidate chains
287
+ if not self.candidate_chains:
288
+ return
317
289
 
318
- # # For each candidate chain, validate blocks and calculate metrics
319
- # for chain_id, data in list(self.candidate_chains.items()):
320
- # latest_candidate_block = data['latest_block']
290
+ # For each candidate chain, validate blocks and calculate metrics
291
+ for chain_id, data in list(self.candidate_chains.items()):
292
+ latest_candidate_block = data['latest_block']
321
293
 
322
- # # Build the chain backwards
323
- # chain_blocks = self._build_chain_from_latest(latest_candidate_block)
294
+ # Build the chain backwards
295
+ chain_blocks = self._build_chain_from_latest(latest_candidate_block)
324
296
 
325
- # # Skip if we couldn't build a complete chain
326
- # if not chain_blocks:
327
- # continue
297
+ # Skip if we couldn't build a complete chain
298
+ if not chain_blocks:
299
+ continue
328
300
 
329
- # # Validate the entire chain
330
- # valid_chain = self._validate_candidate_chain(chain_blocks)
301
+ # Validate the entire chain
302
+ valid_chain = self._validate_candidate_chain(chain_blocks)
331
303
 
332
- # # If valid and better than our current chain, switch to it
333
- # if valid_chain and self._is_better_chain(chain_blocks):
334
- # self._switch_to_new_chain(chain_blocks)
304
+ # If valid and better than our current chain, switch to it
305
+ if valid_chain and self._is_better_chain(chain_blocks):
306
+ self._switch_to_new_chain(chain_blocks)
335
307
 
336
- # def _build_chain_from_latest(self, latest_block: Block) -> List[Block]:
337
- # """
338
- # Build a chain from the latest block back to a known point in our blockchain.
308
+ def _build_chain_from_latest(self, latest_block: Block) -> List[Block]:
309
+ """
310
+ Build a chain from the latest block back to a known point in our blockchain.
339
311
 
340
- # Args:
341
- # latest_block: Latest block in the candidate chain
312
+ Args:
313
+ latest_block: Latest block in the candidate chain
342
314
 
343
- # Returns:
344
- # List of blocks in the chain, ordered from oldest to newest
345
- # """
346
- # chain_blocks = [latest_block]
347
- # current_block = latest_block
348
-
349
- # # Track visited blocks to avoid cycles
350
- # visited = {current_block.get_hash()}
351
-
352
- # # Build chain backwards until we either:
353
- # # 1. Find a block in our main chain
354
- # # 2. Run out of blocks
355
- # # 3. Detect a cycle
356
- # while current_block.number > 0:
357
- # previous_hash = current_block.previous
315
+ Returns:
316
+ List of blocks in the chain, ordered from oldest to newest
317
+ """
318
+ chain_blocks = [latest_block]
319
+ current_block = latest_block
320
+
321
+ # Track visited blocks to avoid cycles
322
+ visited = {current_block.get_hash()}
323
+
324
+ # Build chain backwards until we either:
325
+ # 1. Find a block in our main chain
326
+ # 2. Run out of blocks
327
+ # 3. Detect a cycle
328
+ while current_block.number > 0:
329
+ previous_hash = current_block.previous
358
330
 
359
- # # Check if we have this block in our blockchain
360
- # if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(previous_hash):
361
- # # Found connection to our main chain
362
- # previous_block = self.blockchain.get_block(previous_hash)
363
- # chain_blocks.insert(0, previous_block)
364
- # break
331
+ # Check if we have this block in our blockchain
332
+ if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(previous_hash):
333
+ # Found connection to our main chain
334
+ previous_block = self.blockchain.get_block(previous_hash)
335
+ chain_blocks.insert(0, previous_block)
336
+ break
365
337
 
366
- # # Check if block is in pending blocks
367
- # elif previous_hash in self.pending_blocks:
368
- # previous_block = self.pending_blocks[previous_hash]['block']
338
+ # Check if block is in pending blocks
339
+ elif previous_hash in self.pending_blocks:
340
+ previous_block = self.pending_blocks[previous_hash]['block']
369
341
 
370
- # # Check for cycles
371
- # if previous_hash in visited:
372
- # print(f"Cycle detected in candidate chain at block {previous_block.number}")
373
- # return []
342
+ # Check for cycles
343
+ if previous_hash in visited:
344
+ print(f"Cycle detected in candidate chain at block {previous_block.number}")
345
+ return []
374
346
 
375
- # visited.add(previous_hash)
376
- # chain_blocks.insert(0, previous_block)
377
- # current_block = previous_block
378
- # else:
379
- # # Missing block, cannot validate the chain
380
- # print(f"Missing block {previous_hash.hex()} in candidate chain")
381
- # return []
347
+ visited.add(previous_hash)
348
+ chain_blocks.insert(0, previous_block)
349
+ current_block = previous_block
350
+ else:
351
+ # Missing block, cannot validate the chain
352
+ print(f"Missing block {previous_hash.hex()} in candidate chain")
353
+ return []
382
354
 
383
- # return chain_blocks
355
+ return chain_blocks
384
356
 
385
- # def _validate_candidate_chain(self, chain_blocks: List[Block]) -> bool:
386
- # """
387
- # Validate a candidate chain of blocks.
357
+ def _validate_candidate_chain(self, chain_blocks: List[Block]) -> bool:
358
+ """
359
+ Validate a candidate chain of blocks.
388
360
 
389
- # Args:
390
- # chain_blocks: List of blocks in the chain (oldest to newest)
361
+ Args:
362
+ chain_blocks: List of blocks in the chain (oldest to newest)
391
363
 
392
- # Returns:
393
- # True if the chain is valid, False otherwise
394
- # """
395
- # # Validate each block in the chain
396
- # for i, block in enumerate(chain_blocks):
397
- # # Skip first block, it's either genesis or a block we already have
398
- # if i == 0:
399
- # continue
364
+ Returns:
365
+ True if the chain is valid, False otherwise
366
+ """
367
+ # Validate each block in the chain
368
+ for i, block in enumerate(chain_blocks):
369
+ # Skip first block, it's either genesis or a block we already have
370
+ if i == 0:
371
+ continue
400
372
 
401
- # # Validate block connections
402
- # if block.previous != chain_blocks[i-1].get_hash():
403
- # print(f"Invalid chain: block {block.number} does not reference previous block")
404
- # return False
373
+ # Validate block connections
374
+ if block.previous != chain_blocks[i-1].get_hash():
375
+ print(f"Invalid chain: block {block.number} does not reference previous block")
376
+ return False
405
377
 
406
- # # Validate block
407
- # is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
408
- # if not is_valid:
409
- # print(f"Invalid chain: block {block.number} is invalid")
410
- # return False
378
+ # Validate block
379
+ is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
380
+ if not is_valid:
381
+ print(f"Invalid chain: block {block.number} is invalid")
382
+ return False
411
383
 
412
- # return True
384
+ return True
413
385
 
414
- # def _is_better_chain(self, chain_blocks: List[Block]) -> bool:
415
- # """
416
- # Determine if a candidate chain is better than our current chain.
386
+ def _is_better_chain(self, chain_blocks: List[Block]) -> bool:
387
+ """
388
+ Determine if a candidate chain is better than our current chain.
417
389
 
418
- # Args:
419
- # chain_blocks: List of blocks in the candidate chain
390
+ Args:
391
+ chain_blocks: List of blocks in the candidate chain
420
392
 
421
- # Returns:
422
- # True if the candidate chain is better, False otherwise
423
- # """
424
- # # Get the latest block from the candidate chain
425
- # candidate_latest = chain_blocks[-1]
426
-
427
- # # If we don't have a latest block, any valid chain is better
428
- # if not self.latest_block:
429
- # return True
393
+ Returns:
394
+ True if the candidate chain is better, False otherwise
395
+ """
396
+ # Get the latest block from the candidate chain
397
+ candidate_latest = chain_blocks[-1]
398
+
399
+ # If we don't have a latest block, any valid chain is better
400
+ if not self.latest_block:
401
+ return True
430
402
 
431
- # # Compare block numbers (longest chain rule)
432
- # if candidate_latest.number > self.latest_block.number:
433
- # print(f"Candidate chain is longer: {candidate_latest.number} vs {self.latest_block.number}")
434
- # return True
403
+ # Compare block numbers (longest chain rule)
404
+ if candidate_latest.number > self.latest_block.number:
405
+ print(f"Candidate chain is longer: {candidate_latest.number} vs {self.latest_block.number}")
406
+ return True
435
407
 
436
- # return False
437
-
438
- # def _switch_to_new_chain(self, chain_blocks: List[Block]):
439
- # """
440
- # Switch to a new chain by adding all blocks to our blockchain.
441
-
442
- # Args:
443
- # chain_blocks: List of blocks in the chain (oldest to newest)
444
- # """
445
- # # Find the point where the chains diverge
446
- # divergence_point = 0
447
- # for i, block in enumerate(chain_blocks):
448
- # # Check if we have this block in our blockchain
449
- # if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(block.get_hash()):
450
- # divergence_point = i + 1
451
- # else:
452
- # break
408
+ return False
409
+
410
+ def _switch_to_new_chain(self, chain_blocks: List[Block]):
411
+ """
412
+ Switch to a new chain by adding all blocks to our blockchain.
413
+
414
+ Args:
415
+ chain_blocks: List of blocks in the chain (oldest to newest)
416
+ """
417
+ # Find the point where the chains diverge
418
+ divergence_point = 0
419
+ for i, block in enumerate(chain_blocks):
420
+ # Check if we have this block in our blockchain
421
+ if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(block.get_hash()):
422
+ divergence_point = i + 1
423
+ else:
424
+ break
453
425
 
454
- # # Add all blocks after the divergence point
455
- # for i in range(divergence_point, len(chain_blocks)):
456
- # block = chain_blocks[i]
426
+ # Add all blocks after the divergence point
427
+ for i in range(divergence_point, len(chain_blocks)):
428
+ block = chain_blocks[i]
457
429
 
458
- # # Add block to blockchain
459
- # if hasattr(self.blockchain, 'add_block'):
460
- # try:
461
- # self.blockchain.add_block(block)
430
+ # Add block to blockchain
431
+ if hasattr(self.blockchain, 'add_block'):
432
+ try:
433
+ self.blockchain.add_block(block)
462
434
 
463
- # # Remove from pending blocks
464
- # block_hash = block.get_hash()
465
- # if block_hash in self.pending_blocks:
466
- # del self.pending_blocks[block_hash]
435
+ # Remove from pending blocks
436
+ block_hash = block.get_hash()
437
+ if block_hash in self.pending_blocks:
438
+ del self.pending_blocks[block_hash]
467
439
 
468
- # print(f"Added block {block.number} to blockchain")
469
- # except Exception as e:
470
- # print(f"Error adding block {block.number} to blockchain: {e}")
471
- # return
440
+ print(f"Added block {block.number} to blockchain")
441
+ except Exception as e:
442
+ print(f"Error adding block {block.number} to blockchain: {e}")
443
+ return
472
444
 
473
- # # Update latest block
474
- # self._update_latest_block(chain_blocks[-1])
475
- # print(f"Switched to new chain, latest block: {self.latest_block.number}")
445
+ # Update latest block
446
+ self._update_latest_block(chain_blocks[-1])
447
+ print(f"Switched to new chain, latest block: {self.latest_block.number}")