lunalib 1.6.7__py3-none-any.whl → 1.7.9__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.
- lunalib/.gitignore +3 -0
- lunalib/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/blockchain.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/crypto.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/mempool.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/wallet.cpython-310.pyc +0 -0
- lunalib/core/blockchain.py +182 -2
- lunalib/core/daemon.py +108 -5
- lunalib/core/mempool.py +1 -0
- lunalib/core/wallet.py +856 -492
- lunalib/core/wallet_db.py +68 -0
- lunalib/gtx/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/bill_registry.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/digital_bill.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/genesis.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/cuda_manager.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/difficulty.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/miner.cpython-310.pyc +0 -0
- lunalib/mining/miner.py +33 -14
- lunalib/storage/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/cache.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/database.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/encryption.cpython-310.pyc +0 -0
- lunalib/tests/__pycache__/conftest.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_blockchain.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_crypto.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_gtx.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_mining.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_storage.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_transactions.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_wallet.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/conftest.py +41 -0
- lunalib/tests/init.py +0 -0
- lunalib/tests/integration/__pycache__/test_integration.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/integration/test_integration.py +62 -0
- lunalib/tests/test_blockchain.py +34 -0
- lunalib/tests/test_crypto.py +42 -0
- lunalib/tests/test_gtx.py +135 -0
- lunalib/tests/test_mining.py +244 -0
- lunalib/tests/test_security_suite.py +832 -0
- lunalib/tests/test_storage.py +84 -0
- lunalib/tests/test_transactions.py +103 -0
- lunalib/tests/test_wallet.py +91 -0
- lunalib/transactions/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/security.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/transactions.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/validator.cpython-310.pyc +0 -0
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/METADATA +2 -1
- lunalib-1.7.9.dist-info/RECORD +77 -0
- lunalib-1.6.7.dist-info/RECORD +0 -33
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/WHEEL +0 -0
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/top_level.txt +0 -0
lunalib/.gitignore
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
lunalib/core/blockchain.py
CHANGED
|
@@ -4,17 +4,23 @@ from ..storage.cache import BlockchainCache
|
|
|
4
4
|
import requests
|
|
5
5
|
import time
|
|
6
6
|
import json
|
|
7
|
-
|
|
7
|
+
import asyncio
|
|
8
|
+
import threading
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor, Future
|
|
10
|
+
from typing import Dict, List, Optional, Tuple, Callable
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class BlockchainManager:
|
|
11
14
|
"""Manages blockchain interactions and scanning with transaction broadcasting"""
|
|
12
15
|
|
|
13
|
-
def __init__(self, endpoint_url="https://bank.linglin.art"):
|
|
16
|
+
def __init__(self, endpoint_url="https://bank.linglin.art", max_workers=10):
|
|
14
17
|
self.endpoint_url = endpoint_url.rstrip('/')
|
|
15
18
|
self.cache = BlockchainCache()
|
|
16
19
|
self.network_connected = False
|
|
17
20
|
self._stop_events = [] # Track background monitors so they can be stopped
|
|
21
|
+
self.executor = ThreadPoolExecutor(max_workers=max_workers, thread_name_prefix="BlockchainWorker")
|
|
22
|
+
self._async_tasks = {} # Track async tasks by ID
|
|
23
|
+
self._task_callbacks = {} # Callbacks for task completion
|
|
18
24
|
|
|
19
25
|
# ------------------------------------------------------------------
|
|
20
26
|
# Address helpers
|
|
@@ -26,6 +32,30 @@ class BlockchainManager:
|
|
|
26
32
|
addr_str = str(addr).strip("'\" ").lower()
|
|
27
33
|
return addr_str[4:] if addr_str.startswith('lun_') else addr_str
|
|
28
34
|
|
|
35
|
+
def broadcast_transaction_async(self, transaction: Dict, callback: Callable = None) -> str:
|
|
36
|
+
"""Async version: Broadcast transaction in background thread
|
|
37
|
+
|
|
38
|
+
Returns: task_id that can be used to check status
|
|
39
|
+
"""
|
|
40
|
+
task_id = f"broadcast_{transaction.get('hash', 'unknown')}_{int(time.time())}"
|
|
41
|
+
|
|
42
|
+
def _broadcast_task():
|
|
43
|
+
try:
|
|
44
|
+
success, message = self.broadcast_transaction(transaction)
|
|
45
|
+
if callback:
|
|
46
|
+
callback(success=success, result=message, error=None if success else message)
|
|
47
|
+
return (success, message)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"❌ Async broadcast error: {e}")
|
|
50
|
+
if callback:
|
|
51
|
+
callback(success=False, result=None, error=str(e))
|
|
52
|
+
return (False, str(e))
|
|
53
|
+
|
|
54
|
+
future = self.executor.submit(_broadcast_task)
|
|
55
|
+
self._async_tasks[task_id] = future
|
|
56
|
+
print(f"🔄 Started async broadcast: {task_id}")
|
|
57
|
+
return task_id
|
|
58
|
+
|
|
29
59
|
def broadcast_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
30
60
|
"""Broadcast transaction to mempool with enhanced error handling"""
|
|
31
61
|
try:
|
|
@@ -210,6 +240,30 @@ class BlockchainManager:
|
|
|
210
240
|
|
|
211
241
|
return None
|
|
212
242
|
|
|
243
|
+
def get_blocks_range_async(self, start_height: int, end_height: int, callback: Callable = None) -> str:
|
|
244
|
+
"""Async version: Get range of blocks in background thread
|
|
245
|
+
|
|
246
|
+
Returns: task_id that can be used to check status
|
|
247
|
+
"""
|
|
248
|
+
task_id = f"blocks_range_{start_height}_{end_height}_{int(time.time())}"
|
|
249
|
+
|
|
250
|
+
def _fetch_task():
|
|
251
|
+
try:
|
|
252
|
+
result = self.get_blocks_range(start_height, end_height)
|
|
253
|
+
if callback:
|
|
254
|
+
callback(success=True, result=result, error=None)
|
|
255
|
+
return result
|
|
256
|
+
except Exception as e:
|
|
257
|
+
print(f"❌ Async blocks fetch error: {e}")
|
|
258
|
+
if callback:
|
|
259
|
+
callback(success=False, result=None, error=str(e))
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
future = self.executor.submit(_fetch_task)
|
|
263
|
+
self._async_tasks[task_id] = future
|
|
264
|
+
print(f"🔄 Started async blocks fetch: {task_id}")
|
|
265
|
+
return task_id
|
|
266
|
+
|
|
213
267
|
def get_blocks_range(self, start_height: int, end_height: int) -> List[Dict]:
|
|
214
268
|
"""Get range of blocks"""
|
|
215
269
|
blocks = []
|
|
@@ -286,6 +340,31 @@ class BlockchainManager:
|
|
|
286
340
|
|
|
287
341
|
print(f"[SCAN] Found {len(transactions)} total transactions for {address}")
|
|
288
342
|
return transactions
|
|
343
|
+
|
|
344
|
+
def scan_transactions_for_address_async(self, address: str, callback: Callable = None,
|
|
345
|
+
start_height: int = 0, end_height: int = None) -> str:
|
|
346
|
+
"""Async version: Scan blockchain in background thread, call callback when done
|
|
347
|
+
|
|
348
|
+
Returns: task_id that can be used to check status
|
|
349
|
+
"""
|
|
350
|
+
task_id = f"scan_{address}_{int(time.time())}"
|
|
351
|
+
|
|
352
|
+
def _scan_task():
|
|
353
|
+
try:
|
|
354
|
+
result = self.scan_transactions_for_address(address, start_height, end_height)
|
|
355
|
+
if callback:
|
|
356
|
+
callback(success=True, result=result, error=None)
|
|
357
|
+
return result
|
|
358
|
+
except Exception as e:
|
|
359
|
+
print(f"❌ Async scan error: {e}")
|
|
360
|
+
if callback:
|
|
361
|
+
callback(success=False, result=None, error=str(e))
|
|
362
|
+
return None
|
|
363
|
+
|
|
364
|
+
future = self.executor.submit(_scan_task)
|
|
365
|
+
self._async_tasks[task_id] = future
|
|
366
|
+
print(f"🔄 Started async scan task: {task_id}")
|
|
367
|
+
return task_id
|
|
289
368
|
|
|
290
369
|
def scan_transactions_for_addresses(self, addresses: List[str], start_height: int = 0, end_height: int = None) -> Dict[str, List[Dict]]:
|
|
291
370
|
"""Scan the blockchain once for multiple addresses (rewards and transfers)."""
|
|
@@ -328,7 +407,84 @@ class BlockchainManager:
|
|
|
328
407
|
print(f" - {addr}: {len(results[addr])} transactions")
|
|
329
408
|
|
|
330
409
|
return results
|
|
410
|
+
|
|
411
|
+
def scan_transactions_for_addresses_async(self, addresses: List[str], callback: Callable = None,
|
|
412
|
+
start_height: int = 0, end_height: int = None) -> str:
|
|
413
|
+
"""Async version: Scan blockchain for multiple addresses in background thread
|
|
414
|
+
|
|
415
|
+
Returns: task_id that can be used to check status
|
|
416
|
+
"""
|
|
417
|
+
task_id = f"multi_scan_{int(time.time())}"
|
|
418
|
+
|
|
419
|
+
def _scan_task():
|
|
420
|
+
try:
|
|
421
|
+
result = self.scan_transactions_for_addresses(addresses, start_height, end_height)
|
|
422
|
+
if callback:
|
|
423
|
+
callback(success=True, result=result, error=None)
|
|
424
|
+
return result
|
|
425
|
+
except Exception as e:
|
|
426
|
+
print(f"❌ Async multi-scan error: {e}")
|
|
427
|
+
if callback:
|
|
428
|
+
callback(success=False, result=None, error=str(e))
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
future = self.executor.submit(_scan_task)
|
|
432
|
+
self._async_tasks[task_id] = future
|
|
433
|
+
print(f"🔄 Started async multi-scan task: {task_id}")
|
|
434
|
+
return task_id
|
|
331
435
|
|
|
436
|
+
def get_task_status(self, task_id: str) -> Dict:
|
|
437
|
+
"""Check status of an async task
|
|
438
|
+
|
|
439
|
+
Returns: {'status': 'running'|'completed'|'failed'|'not_found', 'result': any, 'error': str}
|
|
440
|
+
"""
|
|
441
|
+
if task_id not in self._async_tasks:
|
|
442
|
+
return {'status': 'not_found', 'result': None, 'error': 'Task not found'}
|
|
443
|
+
|
|
444
|
+
future = self._async_tasks[task_id]
|
|
445
|
+
|
|
446
|
+
if future.running():
|
|
447
|
+
return {'status': 'running', 'result': None, 'error': None}
|
|
448
|
+
elif future.done():
|
|
449
|
+
try:
|
|
450
|
+
result = future.result(timeout=0)
|
|
451
|
+
return {'status': 'completed', 'result': result, 'error': None}
|
|
452
|
+
except Exception as e:
|
|
453
|
+
return {'status': 'failed', 'result': None, 'error': str(e)}
|
|
454
|
+
else:
|
|
455
|
+
return {'status': 'pending', 'result': None, 'error': None}
|
|
456
|
+
|
|
457
|
+
def cancel_task(self, task_id: str) -> bool:
|
|
458
|
+
"""Cancel a running async task
|
|
459
|
+
|
|
460
|
+
Returns: True if cancelled, False otherwise
|
|
461
|
+
"""
|
|
462
|
+
if task_id in self._async_tasks:
|
|
463
|
+
future = self._async_tasks[task_id]
|
|
464
|
+
if future.cancel():
|
|
465
|
+
del self._async_tasks[task_id]
|
|
466
|
+
print(f"✅ Task cancelled: {task_id}")
|
|
467
|
+
return True
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
def get_active_tasks(self) -> List[str]:
|
|
471
|
+
"""Get list of active task IDs"""
|
|
472
|
+
return [task_id for task_id, future in self._async_tasks.items() if future.running()]
|
|
473
|
+
|
|
474
|
+
def cleanup_completed_tasks(self):
|
|
475
|
+
"""Remove completed tasks from tracking"""
|
|
476
|
+
completed = [task_id for task_id, future in self._async_tasks.items() if future.done()]
|
|
477
|
+
for task_id in completed:
|
|
478
|
+
del self._async_tasks[task_id]
|
|
479
|
+
if completed:
|
|
480
|
+
print(f"🧹 Cleaned up {len(completed)} completed tasks")
|
|
481
|
+
|
|
482
|
+
def shutdown(self):
|
|
483
|
+
"""Shutdown the thread pool executor"""
|
|
484
|
+
print("🛑 Shutting down BlockchainManager executor...")
|
|
485
|
+
self.executor.shutdown(wait=True)
|
|
486
|
+
print("✅ Executor shutdown complete")
|
|
487
|
+
|
|
332
488
|
def monitor_addresses(self, addresses: List[str], on_update, poll_interval: int = 15):
|
|
333
489
|
"""Start background monitor for addresses; returns a stop event."""
|
|
334
490
|
import threading
|
|
@@ -379,6 +535,30 @@ class BlockchainManager:
|
|
|
379
535
|
thread.start()
|
|
380
536
|
return stop_event
|
|
381
537
|
|
|
538
|
+
def submit_mined_block_async(self, block_data: Dict, callback: Callable = None) -> str:
|
|
539
|
+
"""Async version: Submit mined block in background thread
|
|
540
|
+
|
|
541
|
+
Returns: task_id that can be used to check status
|
|
542
|
+
"""
|
|
543
|
+
task_id = f"submit_block_{block_data.get('index', 'unknown')}_{int(time.time())}"
|
|
544
|
+
|
|
545
|
+
def _submit_task():
|
|
546
|
+
try:
|
|
547
|
+
success = self.submit_mined_block(block_data)
|
|
548
|
+
if callback:
|
|
549
|
+
callback(success=success, result=block_data if success else None, error=None if success else "Submission failed")
|
|
550
|
+
return success
|
|
551
|
+
except Exception as e:
|
|
552
|
+
print(f"❌ Async block submission error: {e}")
|
|
553
|
+
if callback:
|
|
554
|
+
callback(success=False, result=None, error=str(e))
|
|
555
|
+
return False
|
|
556
|
+
|
|
557
|
+
future = self.executor.submit(_submit_task)
|
|
558
|
+
self._async_tasks[task_id] = future
|
|
559
|
+
print(f"🔄 Started async block submission: {task_id}")
|
|
560
|
+
return task_id
|
|
561
|
+
|
|
382
562
|
def submit_mined_block(self, block_data: Dict) -> bool:
|
|
383
563
|
"""Submit a mined block to the network with built-in validation"""
|
|
384
564
|
try:
|
lunalib/core/daemon.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# lunalib/core/daemon.py
|
|
2
2
|
import time
|
|
3
3
|
import threading
|
|
4
|
-
from
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
from typing import Dict, List, Optional, Callable
|
|
5
6
|
import json
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
|
|
@@ -12,7 +13,7 @@ class BlockchainDaemon:
|
|
|
12
13
|
Validates all transactions, manages peer registry, and serves as source of truth.
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
|
-
def __init__(self, blockchain_manager, mempool_manager, security_manager=None):
|
|
16
|
+
def __init__(self, blockchain_manager, mempool_manager, security_manager=None, max_workers=5):
|
|
16
17
|
self.blockchain = blockchain_manager
|
|
17
18
|
self.mempool = mempool_manager
|
|
18
19
|
self.security = security_manager
|
|
@@ -21,6 +22,10 @@ class BlockchainDaemon:
|
|
|
21
22
|
from ..mining.difficulty import DifficultySystem
|
|
22
23
|
self.difficulty_system = DifficultySystem()
|
|
23
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
|
+
|
|
24
29
|
# Peer registry
|
|
25
30
|
self.peers = {} # {node_id: peer_info}
|
|
26
31
|
self.peer_lock = threading.Lock()
|
|
@@ -64,6 +69,10 @@ class BlockchainDaemon:
|
|
|
64
69
|
if self.cleanup_thread:
|
|
65
70
|
self.cleanup_thread.join(timeout=5)
|
|
66
71
|
|
|
72
|
+
# Shutdown executor
|
|
73
|
+
print("🛑 Shutting down daemon executor...")
|
|
74
|
+
self.executor.shutdown(wait=True)
|
|
75
|
+
|
|
67
76
|
print("🛑 Blockchain Daemon stopped")
|
|
68
77
|
|
|
69
78
|
def register_peer(self, peer_info: Dict) -> Dict:
|
|
@@ -140,6 +149,30 @@ class BlockchainDaemon:
|
|
|
140
149
|
if node_id in self.peers:
|
|
141
150
|
self.peers[node_id]['last_seen'] = time.time()
|
|
142
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
|
+
|
|
143
176
|
def validate_block(self, block: Dict) -> Dict:
|
|
144
177
|
"""
|
|
145
178
|
Validate a block submitted by a peer or miner.
|
|
@@ -174,13 +207,26 @@ class BlockchainDaemon:
|
|
|
174
207
|
if not self.difficulty_system.validate_block_hash(block_hash, difficulty):
|
|
175
208
|
errors.append(f"Hash doesn't meet difficulty {difficulty} requirement (needs {difficulty} leading zeros)")
|
|
176
209
|
|
|
177
|
-
# Validate
|
|
178
|
-
|
|
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
|
+
|
|
179
225
|
actual_reward = block.get('reward', 0)
|
|
180
226
|
|
|
181
227
|
# Allow small tolerance for floating point comparison
|
|
182
228
|
if abs(actual_reward - expected_reward) > 0.01:
|
|
183
|
-
errors.append(f"Reward mismatch: expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC")
|
|
229
|
+
errors.append(f"Reward mismatch ({reward_type}): expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC")
|
|
184
230
|
|
|
185
231
|
# Validate transactions
|
|
186
232
|
for tx in block.get('transactions', []):
|
|
@@ -251,6 +297,30 @@ class BlockchainDaemon:
|
|
|
251
297
|
except Exception as e:
|
|
252
298
|
return {'valid': False, 'message': f'Validation error: {str(e)}', 'errors': [str(e)]}
|
|
253
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
|
+
|
|
254
324
|
def process_incoming_block(self, block: Dict, from_peer: Optional[str] = None) -> Dict:
|
|
255
325
|
"""
|
|
256
326
|
Process an incoming block from P2P network.
|
|
@@ -362,6 +432,39 @@ class BlockchainDaemon:
|
|
|
362
432
|
print(f"❌ Cleanup loop error: {e}")
|
|
363
433
|
time.sleep(60)
|
|
364
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
|
+
|
|
365
468
|
def get_stats(self) -> Dict:
|
|
366
469
|
"""Get daemon statistics"""
|
|
367
470
|
uptime = time.time() - self.stats['start_time']
|
lunalib/core/mempool.py
CHANGED
|
@@ -211,6 +211,7 @@ class MempoolManager:
|
|
|
211
211
|
to_norm = self._normalize_address(tx.get('to') or tx.get('receiver'))
|
|
212
212
|
if target_norm and (from_norm == target_norm or to_norm == target_norm):
|
|
213
213
|
transactions.append(tx)
|
|
214
|
+
print(f"[MEMPOOL] get_pending_transactions for {address}: {len(transactions)} txs returned")
|
|
214
215
|
return transactions
|
|
215
216
|
|
|
216
217
|
def get_pending_transactions_for_addresses(self, addresses: List[str], fetch_remote: bool = True) -> Dict[str, List[Dict]]:
|