lunalib 1.5.1__py3-none-any.whl โ 1.7.2__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 lunalib might be problematic. Click here for more details.
- lunalib/core/__init__.py +14 -0
- lunalib/core/blockchain.py +183 -3
- lunalib/core/daemon.py +494 -0
- lunalib/core/p2p.py +361 -0
- lunalib/core/sm2.py +723 -723
- lunalib/core/wallet.py +714 -478
- lunalib/core/wallet_manager.py +638 -638
- lunalib/core/wallet_sync_helper.py +163 -163
- lunalib/gtx/digital_bill.py +10 -2
- lunalib/gtx/genesis.py +23 -24
- lunalib/mining/__init__.py +5 -0
- lunalib/mining/cuda_manager.py +23 -28
- lunalib/mining/difficulty.py +38 -0
- lunalib/mining/miner.py +526 -17
- lunalib/storage/cache.py +13 -4
- lunalib/storage/database.py +14 -5
- lunalib/storage/encryption.py +11 -2
- lunalib/transactions/security.py +19 -10
- lunalib/transactions/transactions.py +50 -41
- lunalib-1.7.2.dist-info/METADATA +27 -0
- lunalib-1.7.2.dist-info/RECORD +33 -0
- lunalib-1.7.2.dist-info/top_level.txt +1 -0
- core/__init__.py +0 -0
- core/blockchain.py +0 -172
- core/crypto.py +0 -32
- core/wallet.py +0 -408
- gtx/__init__.py +0 -0
- gtx/bill_registry.py +0 -122
- gtx/digital_bill.py +0 -273
- gtx/genesis.py +0 -338
- lunalib/requirements.txt +0 -44
- lunalib-1.5.1.dist-info/METADATA +0 -283
- lunalib-1.5.1.dist-info/RECORD +0 -53
- lunalib-1.5.1.dist-info/entry_points.txt +0 -2
- lunalib-1.5.1.dist-info/top_level.txt +0 -6
- mining/__init__.py +0 -0
- mining/cuda_manager.py +0 -137
- mining/difficulty.py +0 -106
- mining/miner.py +0 -107
- storage/__init__.py +0 -0
- storage/cache.py +0 -148
- storage/database.py +0 -222
- storage/encryption.py +0 -105
- transactions/__init__.py +0 -0
- transactions/security.py +0 -172
- transactions/transactions.py +0 -424
- transactions/validator.py +0 -71
- {lunalib-1.5.1.dist-info โ lunalib-1.7.2.dist-info}/WHEEL +0 -0
lunalib/core/daemon.py
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# lunalib/core/daemon.py
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
from typing import Dict, List, Optional, Callable
|
|
6
|
+
import json
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlockchainDaemon:
|
|
11
|
+
"""
|
|
12
|
+
Primary blockchain daemon that manages the authoritative blockchain state.
|
|
13
|
+
Validates all transactions, manages peer registry, and serves as source of truth.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, blockchain_manager, mempool_manager, security_manager=None, max_workers=5):
|
|
17
|
+
self.blockchain = blockchain_manager
|
|
18
|
+
self.mempool = mempool_manager
|
|
19
|
+
self.security = security_manager
|
|
20
|
+
|
|
21
|
+
# Initialize difficulty system for validation
|
|
22
|
+
from ..mining.difficulty import DifficultySystem
|
|
23
|
+
self.difficulty_system = DifficultySystem()
|
|
24
|
+
|
|
25
|
+
# Thread pool for async operations
|
|
26
|
+
self.executor = ThreadPoolExecutor(max_workers=max_workers, thread_name_prefix="DaemonWorker")
|
|
27
|
+
self._async_tasks = {} # Track async tasks
|
|
28
|
+
|
|
29
|
+
# Peer registry
|
|
30
|
+
self.peers = {} # {node_id: peer_info}
|
|
31
|
+
self.peer_lock = threading.Lock()
|
|
32
|
+
|
|
33
|
+
# Daemon state
|
|
34
|
+
self.is_running = False
|
|
35
|
+
self.validation_thread = None
|
|
36
|
+
self.cleanup_thread = None
|
|
37
|
+
|
|
38
|
+
# Statistics
|
|
39
|
+
self.stats = {
|
|
40
|
+
'blocks_validated': 0,
|
|
41
|
+
'transactions_validated': 0,
|
|
42
|
+
'peers_registered': 0,
|
|
43
|
+
'start_time': time.time()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def start(self):
|
|
47
|
+
"""Start the blockchain daemon"""
|
|
48
|
+
if self.is_running:
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
self.is_running = True
|
|
52
|
+
print("๐๏ธ Blockchain Daemon starting...")
|
|
53
|
+
|
|
54
|
+
# Start background threads
|
|
55
|
+
self.validation_thread = threading.Thread(target=self._validation_loop, daemon=True)
|
|
56
|
+
self.validation_thread.start()
|
|
57
|
+
|
|
58
|
+
self.cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True)
|
|
59
|
+
self.cleanup_thread.start()
|
|
60
|
+
|
|
61
|
+
print("โ
Blockchain Daemon started")
|
|
62
|
+
|
|
63
|
+
def stop(self):
|
|
64
|
+
"""Stop the blockchain daemon"""
|
|
65
|
+
self.is_running = False
|
|
66
|
+
|
|
67
|
+
if self.validation_thread:
|
|
68
|
+
self.validation_thread.join(timeout=5)
|
|
69
|
+
if self.cleanup_thread:
|
|
70
|
+
self.cleanup_thread.join(timeout=5)
|
|
71
|
+
|
|
72
|
+
# Shutdown executor
|
|
73
|
+
print("๐ Shutting down daemon executor...")
|
|
74
|
+
self.executor.shutdown(wait=True)
|
|
75
|
+
|
|
76
|
+
print("๐ Blockchain Daemon stopped")
|
|
77
|
+
|
|
78
|
+
def register_peer(self, peer_info: Dict) -> Dict:
|
|
79
|
+
"""
|
|
80
|
+
Register a new peer node.
|
|
81
|
+
Returns: {'success': bool, 'node_id': str, 'message': str}
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
node_id = peer_info.get('node_id')
|
|
85
|
+
if not node_id:
|
|
86
|
+
return {'success': False, 'message': 'Missing node_id'}
|
|
87
|
+
|
|
88
|
+
with self.peer_lock:
|
|
89
|
+
self.peers[node_id] = {
|
|
90
|
+
'node_id': node_id,
|
|
91
|
+
'registered_at': time.time(),
|
|
92
|
+
'last_seen': time.time(),
|
|
93
|
+
'capabilities': peer_info.get('capabilities', []),
|
|
94
|
+
'url': peer_info.get('url'),
|
|
95
|
+
'version': peer_info.get('version', 'unknown')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
self.stats['peers_registered'] += 1
|
|
99
|
+
|
|
100
|
+
print(f"๐ค New peer registered: {node_id}")
|
|
101
|
+
return {
|
|
102
|
+
'success': True,
|
|
103
|
+
'node_id': node_id,
|
|
104
|
+
'message': 'Peer registered successfully'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
return {'success': False, 'message': str(e)}
|
|
109
|
+
|
|
110
|
+
def unregister_peer(self, node_id: str) -> Dict:
|
|
111
|
+
"""
|
|
112
|
+
Unregister a peer node.
|
|
113
|
+
Returns: {'success': bool, 'message': str}
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
with self.peer_lock:
|
|
117
|
+
if node_id in self.peers:
|
|
118
|
+
del self.peers[node_id]
|
|
119
|
+
print(f"๐ Peer unregistered: {node_id}")
|
|
120
|
+
return {'success': True, 'message': 'Peer unregistered'}
|
|
121
|
+
else:
|
|
122
|
+
return {'success': False, 'message': 'Peer not found'}
|
|
123
|
+
except Exception as e:
|
|
124
|
+
return {'success': False, 'message': str(e)}
|
|
125
|
+
|
|
126
|
+
def get_peer_list(self, exclude_node_id: Optional[str] = None) -> List[Dict]:
|
|
127
|
+
"""
|
|
128
|
+
Get list of registered peers.
|
|
129
|
+
exclude_node_id: Optional node ID to exclude from results
|
|
130
|
+
"""
|
|
131
|
+
with self.peer_lock:
|
|
132
|
+
peer_list = []
|
|
133
|
+
for node_id, peer_info in self.peers.items():
|
|
134
|
+
if exclude_node_id and node_id == exclude_node_id:
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
peer_list.append({
|
|
138
|
+
'node_id': peer_info['node_id'],
|
|
139
|
+
'url': peer_info.get('url'),
|
|
140
|
+
'last_seen': peer_info['last_seen'],
|
|
141
|
+
'capabilities': peer_info.get('capabilities', [])
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return peer_list
|
|
145
|
+
|
|
146
|
+
def update_peer_heartbeat(self, node_id: str):
|
|
147
|
+
"""Update peer's last seen timestamp"""
|
|
148
|
+
with self.peer_lock:
|
|
149
|
+
if node_id in self.peers:
|
|
150
|
+
self.peers[node_id]['last_seen'] = time.time()
|
|
151
|
+
|
|
152
|
+
def validate_block_async(self, block: Dict, callback: Callable = None) -> str:
|
|
153
|
+
"""Async version: Validate block in background thread
|
|
154
|
+
|
|
155
|
+
Returns: task_id that can be used to check status
|
|
156
|
+
"""
|
|
157
|
+
task_id = f"validate_block_{block.get('index', 'unknown')}_{int(time.time())}"
|
|
158
|
+
|
|
159
|
+
def _validate_task():
|
|
160
|
+
try:
|
|
161
|
+
result = self.validate_block(block)
|
|
162
|
+
if callback:
|
|
163
|
+
callback(success=result['valid'], result=result, error=None if result['valid'] else result['message'])
|
|
164
|
+
return result
|
|
165
|
+
except Exception as e:
|
|
166
|
+
print(f"โ Async validation error: {e}")
|
|
167
|
+
if callback:
|
|
168
|
+
callback(success=False, result=None, error=str(e))
|
|
169
|
+
return {'valid': False, 'message': str(e), 'errors': [str(e)]}
|
|
170
|
+
|
|
171
|
+
future = self.executor.submit(_validate_task)
|
|
172
|
+
self._async_tasks[task_id] = future
|
|
173
|
+
print(f"๐ Started async block validation: {task_id}")
|
|
174
|
+
return task_id
|
|
175
|
+
|
|
176
|
+
def validate_block(self, block: Dict) -> Dict:
|
|
177
|
+
"""
|
|
178
|
+
Validate a block submitted by a peer or miner.
|
|
179
|
+
Returns: {'valid': bool, 'message': str, 'errors': List[str]}
|
|
180
|
+
"""
|
|
181
|
+
errors = []
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
# Basic structure validation using difficulty system
|
|
185
|
+
is_valid, error_msg = self.difficulty_system.validate_block_structure(block)
|
|
186
|
+
if not is_valid:
|
|
187
|
+
errors.append(error_msg)
|
|
188
|
+
return {'valid': False, 'message': 'Invalid block structure', 'errors': errors}
|
|
189
|
+
|
|
190
|
+
# Validate block index
|
|
191
|
+
latest_block = self.blockchain.get_latest_block()
|
|
192
|
+
if latest_block:
|
|
193
|
+
expected_index = latest_block.get('index', 0) + 1
|
|
194
|
+
if block['index'] != expected_index:
|
|
195
|
+
errors.append(f"Invalid block index: expected {expected_index}, got {block['index']}")
|
|
196
|
+
|
|
197
|
+
# Validate previous hash
|
|
198
|
+
if latest_block and block['previous_hash'] != latest_block.get('hash'):
|
|
199
|
+
expected_hash = latest_block.get('hash', '')
|
|
200
|
+
actual_hash = block.get('previous_hash', '')
|
|
201
|
+
errors.append(f"Previous hash mismatch: expected {expected_hash[:16]}..., got {actual_hash[:16]}...")
|
|
202
|
+
|
|
203
|
+
# Validate hash meets difficulty requirement using difficulty system
|
|
204
|
+
difficulty = block.get('difficulty', 0)
|
|
205
|
+
block_hash = block.get('hash', '')
|
|
206
|
+
|
|
207
|
+
if not self.difficulty_system.validate_block_hash(block_hash, difficulty):
|
|
208
|
+
errors.append(f"Hash doesn't meet difficulty {difficulty} requirement (needs {difficulty} leading zeros)")
|
|
209
|
+
|
|
210
|
+
# Validate reward matches difficulty
|
|
211
|
+
# Empty blocks use LINEAR system (difficulty = reward)
|
|
212
|
+
# Regular blocks use EXPONENTIAL system (10^(difficulty-1))
|
|
213
|
+
transactions = block.get('transactions', [])
|
|
214
|
+
is_empty_block = len(transactions) == 1 and transactions[0].get('is_empty_block', False)
|
|
215
|
+
|
|
216
|
+
if is_empty_block:
|
|
217
|
+
# Empty block: linear reward (difficulty 1 = 1 LKC, difficulty 2 = 2 LKC, etc.)
|
|
218
|
+
expected_reward = float(difficulty)
|
|
219
|
+
reward_type = "Linear"
|
|
220
|
+
else:
|
|
221
|
+
# Regular block: exponential reward (10^(difficulty-1))
|
|
222
|
+
expected_reward = self.difficulty_system.calculate_block_reward(difficulty)
|
|
223
|
+
reward_type = "Exponential"
|
|
224
|
+
|
|
225
|
+
actual_reward = block.get('reward', 0)
|
|
226
|
+
|
|
227
|
+
# Allow small tolerance for floating point comparison
|
|
228
|
+
if abs(actual_reward - expected_reward) > 0.01:
|
|
229
|
+
errors.append(f"Reward mismatch ({reward_type}): expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC")
|
|
230
|
+
|
|
231
|
+
# Validate transactions
|
|
232
|
+
for tx in block.get('transactions', []):
|
|
233
|
+
tx_validation = self.validate_transaction(tx)
|
|
234
|
+
if not tx_validation['valid']:
|
|
235
|
+
errors.append(f"Invalid transaction: {tx_validation['message']}")
|
|
236
|
+
|
|
237
|
+
if errors:
|
|
238
|
+
return {'valid': False, 'message': 'Block validation failed', 'errors': errors}
|
|
239
|
+
|
|
240
|
+
self.stats['blocks_validated'] += 1
|
|
241
|
+
print(f"โ
Block #{block['index']} validated: Difficulty {difficulty}, Reward {actual_reward} LKC, Hash {block_hash[:16]}...")
|
|
242
|
+
return {'valid': True, 'message': 'Block is valid', 'errors': []}
|
|
243
|
+
|
|
244
|
+
except Exception as e:
|
|
245
|
+
return {'valid': False, 'message': f'Validation error: {str(e)}', 'errors': [str(e)]}
|
|
246
|
+
|
|
247
|
+
def validate_transaction(self, transaction: Dict) -> Dict:
|
|
248
|
+
"""
|
|
249
|
+
Validate a transaction submitted by a peer.
|
|
250
|
+
Returns: {'valid': bool, 'message': str, 'errors': List[str]}
|
|
251
|
+
"""
|
|
252
|
+
errors = []
|
|
253
|
+
|
|
254
|
+
try:
|
|
255
|
+
# Basic validation
|
|
256
|
+
tx_type = transaction.get('type')
|
|
257
|
+
if not tx_type:
|
|
258
|
+
errors.append("Missing transaction type")
|
|
259
|
+
|
|
260
|
+
# Type-specific validation
|
|
261
|
+
if tx_type == 'transaction':
|
|
262
|
+
required = ['from', 'to', 'amount', 'timestamp']
|
|
263
|
+
for field in required:
|
|
264
|
+
if field not in transaction:
|
|
265
|
+
errors.append(f"Missing field: {field}")
|
|
266
|
+
|
|
267
|
+
# Validate amount
|
|
268
|
+
amount = transaction.get('amount', 0)
|
|
269
|
+
if amount <= 0:
|
|
270
|
+
errors.append("Invalid amount: must be positive")
|
|
271
|
+
|
|
272
|
+
elif tx_type == 'genesis_bill':
|
|
273
|
+
if 'denomination' not in transaction:
|
|
274
|
+
errors.append("Missing denomination for genesis bill")
|
|
275
|
+
|
|
276
|
+
elif tx_type == 'reward':
|
|
277
|
+
required = ['to', 'amount']
|
|
278
|
+
for field in required:
|
|
279
|
+
if field not in transaction:
|
|
280
|
+
errors.append(f"Missing field: {field}")
|
|
281
|
+
|
|
282
|
+
# Security validation if available
|
|
283
|
+
if self.security and not errors:
|
|
284
|
+
try:
|
|
285
|
+
# Use security manager for advanced validation
|
|
286
|
+
# security_result = self.security.validate_transaction(transaction)
|
|
287
|
+
pass
|
|
288
|
+
except Exception as e:
|
|
289
|
+
errors.append(f"Security validation error: {e}")
|
|
290
|
+
|
|
291
|
+
if errors:
|
|
292
|
+
return {'valid': False, 'message': 'Transaction validation failed', 'errors': errors}
|
|
293
|
+
|
|
294
|
+
self.stats['transactions_validated'] += 1
|
|
295
|
+
return {'valid': True, 'message': 'Transaction is valid', 'errors': []}
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
return {'valid': False, 'message': f'Validation error: {str(e)}', 'errors': [str(e)]}
|
|
299
|
+
|
|
300
|
+
def process_incoming_block_async(self, block: Dict, from_peer: Optional[str] = None, callback: Callable = None) -> str:
|
|
301
|
+
"""Async version: Process incoming block in background thread
|
|
302
|
+
|
|
303
|
+
Returns: task_id that can be used to check status
|
|
304
|
+
"""
|
|
305
|
+
task_id = f"process_block_{block.get('index', 'unknown')}_{int(time.time())}"
|
|
306
|
+
|
|
307
|
+
def _process_task():
|
|
308
|
+
try:
|
|
309
|
+
result = self.process_incoming_block(block, from_peer)
|
|
310
|
+
if callback:
|
|
311
|
+
callback(success=result.get('success', False), result=result, error=None if result.get('success') else result.get('message'))
|
|
312
|
+
return result
|
|
313
|
+
except Exception as e:
|
|
314
|
+
print(f"โ Async block processing error: {e}")
|
|
315
|
+
if callback:
|
|
316
|
+
callback(success=False, result=None, error=str(e))
|
|
317
|
+
return {'success': False, 'message': str(e)}
|
|
318
|
+
|
|
319
|
+
future = self.executor.submit(_process_task)
|
|
320
|
+
self._async_tasks[task_id] = future
|
|
321
|
+
print(f"๐ Started async block processing: {task_id}")
|
|
322
|
+
return task_id
|
|
323
|
+
|
|
324
|
+
def process_incoming_block(self, block: Dict, from_peer: Optional[str] = None) -> Dict:
|
|
325
|
+
"""
|
|
326
|
+
Process an incoming block from P2P network.
|
|
327
|
+
Validates and adds to blockchain if valid.
|
|
328
|
+
"""
|
|
329
|
+
try:
|
|
330
|
+
# Validate block
|
|
331
|
+
validation = self.validate_block(block)
|
|
332
|
+
|
|
333
|
+
if not validation['valid']:
|
|
334
|
+
print(f"โ Invalid block from peer {from_peer}: {validation['message']}")
|
|
335
|
+
return validation
|
|
336
|
+
|
|
337
|
+
# Add to blockchain
|
|
338
|
+
success = self.blockchain.submit_mined_block(block)
|
|
339
|
+
|
|
340
|
+
if success:
|
|
341
|
+
print(f"โ
Block #{block['index']} accepted from peer {from_peer}")
|
|
342
|
+
|
|
343
|
+
# Broadcast to other peers
|
|
344
|
+
self._broadcast_block_to_peers(block, exclude=from_peer)
|
|
345
|
+
|
|
346
|
+
return {'success': True, 'message': 'Block accepted and propagated'}
|
|
347
|
+
else:
|
|
348
|
+
return {'success': False, 'message': 'Block submission failed'}
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
return {'success': False, 'message': f'Processing error: {str(e)}'}
|
|
352
|
+
|
|
353
|
+
def process_incoming_transaction(self, transaction: Dict, from_peer: Optional[str] = None) -> Dict:
|
|
354
|
+
"""
|
|
355
|
+
Process an incoming transaction from P2P network.
|
|
356
|
+
Validates and adds to mempool if valid.
|
|
357
|
+
"""
|
|
358
|
+
try:
|
|
359
|
+
# Validate transaction
|
|
360
|
+
validation = self.validate_transaction(transaction)
|
|
361
|
+
|
|
362
|
+
if not validation['valid']:
|
|
363
|
+
print(f"โ Invalid transaction from peer {from_peer}: {validation['message']}")
|
|
364
|
+
return validation
|
|
365
|
+
|
|
366
|
+
# Add to mempool
|
|
367
|
+
# self.mempool.add_transaction(transaction)
|
|
368
|
+
print(f"โ
Transaction accepted from peer {from_peer}")
|
|
369
|
+
|
|
370
|
+
# Broadcast to other peers
|
|
371
|
+
self._broadcast_transaction_to_peers(transaction, exclude=from_peer)
|
|
372
|
+
|
|
373
|
+
return {'success': True, 'message': 'Transaction accepted and propagated'}
|
|
374
|
+
|
|
375
|
+
except Exception as e:
|
|
376
|
+
return {'success': False, 'message': f'Processing error: {str(e)}'}
|
|
377
|
+
|
|
378
|
+
def _broadcast_block_to_peers(self, block: Dict, exclude: Optional[str] = None):
|
|
379
|
+
"""Broadcast block to all registered peers except excluded one"""
|
|
380
|
+
with self.peer_lock:
|
|
381
|
+
for node_id, peer_info in self.peers.items():
|
|
382
|
+
if node_id == exclude:
|
|
383
|
+
continue
|
|
384
|
+
|
|
385
|
+
# Send block to peer (implementation depends on transport)
|
|
386
|
+
# This would typically use HTTP POST or WebSocket
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
def _broadcast_transaction_to_peers(self, transaction: Dict, exclude: Optional[str] = None):
|
|
390
|
+
"""Broadcast transaction to all registered peers except excluded one"""
|
|
391
|
+
with self.peer_lock:
|
|
392
|
+
for node_id, peer_info in self.peers.items():
|
|
393
|
+
if node_id == exclude:
|
|
394
|
+
continue
|
|
395
|
+
|
|
396
|
+
# Send transaction to peer
|
|
397
|
+
pass
|
|
398
|
+
|
|
399
|
+
def _validation_loop(self):
|
|
400
|
+
"""Background thread for continuous validation"""
|
|
401
|
+
while self.is_running:
|
|
402
|
+
try:
|
|
403
|
+
# Periodic mempool validation
|
|
404
|
+
# Remove invalid transactions from mempool
|
|
405
|
+
time.sleep(30)
|
|
406
|
+
|
|
407
|
+
except Exception as e:
|
|
408
|
+
print(f"โ Validation loop error: {e}")
|
|
409
|
+
time.sleep(10)
|
|
410
|
+
|
|
411
|
+
def _cleanup_loop(self):
|
|
412
|
+
"""Background thread for peer cleanup"""
|
|
413
|
+
while self.is_running:
|
|
414
|
+
try:
|
|
415
|
+
current_time = time.time()
|
|
416
|
+
timeout = 300 # 5 minutes
|
|
417
|
+
|
|
418
|
+
with self.peer_lock:
|
|
419
|
+
inactive_peers = []
|
|
420
|
+
for node_id, peer_info in self.peers.items():
|
|
421
|
+
if current_time - peer_info['last_seen'] > timeout:
|
|
422
|
+
inactive_peers.append(node_id)
|
|
423
|
+
|
|
424
|
+
# Remove inactive peers
|
|
425
|
+
for node_id in inactive_peers:
|
|
426
|
+
del self.peers[node_id]
|
|
427
|
+
print(f"๐งน Removed inactive peer: {node_id}")
|
|
428
|
+
|
|
429
|
+
time.sleep(60) # Check every minute
|
|
430
|
+
|
|
431
|
+
except Exception as e:
|
|
432
|
+
print(f"โ Cleanup loop error: {e}")
|
|
433
|
+
time.sleep(60)
|
|
434
|
+
|
|
435
|
+
def get_task_status(self, task_id: str) -> Dict:
|
|
436
|
+
"""Check status of an async task
|
|
437
|
+
|
|
438
|
+
Returns: {'status': 'running'|'completed'|'failed'|'not_found', 'result': any, 'error': str}
|
|
439
|
+
"""
|
|
440
|
+
if task_id not in self._async_tasks:
|
|
441
|
+
return {'status': 'not_found', 'result': None, 'error': 'Task not found'}
|
|
442
|
+
|
|
443
|
+
future = self._async_tasks[task_id]
|
|
444
|
+
|
|
445
|
+
if future.running():
|
|
446
|
+
return {'status': 'running', 'result': None, 'error': None}
|
|
447
|
+
elif future.done():
|
|
448
|
+
try:
|
|
449
|
+
result = future.result(timeout=0)
|
|
450
|
+
return {'status': 'completed', 'result': result, 'error': None}
|
|
451
|
+
except Exception as e:
|
|
452
|
+
return {'status': 'failed', 'result': None, 'error': str(e)}
|
|
453
|
+
else:
|
|
454
|
+
return {'status': 'pending', 'result': None, 'error': None}
|
|
455
|
+
|
|
456
|
+
def get_active_tasks(self) -> List[str]:
|
|
457
|
+
"""Get list of active task IDs"""
|
|
458
|
+
return [task_id for task_id, future in self._async_tasks.items() if future.running()]
|
|
459
|
+
|
|
460
|
+
def cleanup_completed_tasks(self):
|
|
461
|
+
"""Remove completed tasks from tracking"""
|
|
462
|
+
completed = [task_id for task_id, future in self._async_tasks.items() if future.done()]
|
|
463
|
+
for task_id in completed:
|
|
464
|
+
del self._async_tasks[task_id]
|
|
465
|
+
if completed:
|
|
466
|
+
print(f"๐งน Cleaned up {len(completed)} completed tasks")
|
|
467
|
+
|
|
468
|
+
def get_stats(self) -> Dict:
|
|
469
|
+
"""Get daemon statistics"""
|
|
470
|
+
uptime = time.time() - self.stats['start_time']
|
|
471
|
+
|
|
472
|
+
with self.peer_lock:
|
|
473
|
+
peer_count = len(self.peers)
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
'uptime_seconds': uptime,
|
|
477
|
+
'blocks_validated': self.stats['blocks_validated'],
|
|
478
|
+
'transactions_validated': self.stats['transactions_validated'],
|
|
479
|
+
'peers_registered': self.stats['peers_registered'],
|
|
480
|
+
'active_peers': peer_count,
|
|
481
|
+
'mempool_size': len(self.mempool.get_pending_transactions()) if self.mempool else 0
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
def get_blockchain_state(self) -> Dict:
|
|
485
|
+
"""Get current blockchain state for peers"""
|
|
486
|
+
latest_block = self.blockchain.get_latest_block()
|
|
487
|
+
height = self.blockchain.get_blockchain_height()
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
'height': height,
|
|
491
|
+
'latest_block': latest_block,
|
|
492
|
+
'mempool_size': len(self.mempool.get_pending_transactions()) if self.mempool else 0,
|
|
493
|
+
'timestamp': time.time()
|
|
494
|
+
}
|