lunalib 1.5.2__py3-none-any.whl → 1.6.6__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/core/__init__.py +14 -0
- lunalib/core/blockchain.py +1 -1
- lunalib/core/daemon.py +391 -0
- lunalib/core/p2p.py +346 -0
- 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 +485 -4
- lunalib/transactions/security.py +8 -8
- lunalib/transactions/transactions.py +27 -27
- {lunalib-1.5.2.dist-info → lunalib-1.6.6.dist-info}/METADATA +7 -1
- {lunalib-1.5.2.dist-info → lunalib-1.6.6.dist-info}/RECORD +16 -14
- {lunalib-1.5.2.dist-info → lunalib-1.6.6.dist-info}/WHEEL +0 -0
- {lunalib-1.5.2.dist-info → lunalib-1.6.6.dist-info}/top_level.txt +0 -0
lunalib/core/__init__.py
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .blockchain import BlockchainManager
|
|
2
|
+
from .wallet import LunaWallet
|
|
3
|
+
from .mempool import MempoolManager
|
|
4
|
+
from .p2p import P2PClient, HybridBlockchainClient
|
|
5
|
+
from .daemon import BlockchainDaemon
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'BlockchainManager',
|
|
9
|
+
'LunaWallet',
|
|
10
|
+
'MempoolManager',
|
|
11
|
+
'P2PClient',
|
|
12
|
+
'HybridBlockchainClient',
|
|
13
|
+
'BlockchainDaemon'
|
|
14
|
+
]
|
lunalib/core/blockchain.py
CHANGED
|
@@ -736,7 +736,7 @@ class BlockchainManager:
|
|
|
736
736
|
transactions.append(enhanced_tx)
|
|
737
737
|
print(f"⬇️ Found outgoing transaction: {amount} LUN + {fee} fee")
|
|
738
738
|
|
|
739
|
-
print(f"
|
|
739
|
+
print(f" Scan complete for block #{block.get('index')}: {len(transactions)} transactions found")
|
|
740
740
|
return transactions
|
|
741
741
|
def _handle_regular_transfers(self, tx: Dict, address_lower: str) -> Dict:
|
|
742
742
|
"""Handle regular transfer transactions that might be in different formats"""
|
lunalib/core/daemon.py
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# lunalib/core/daemon.py
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
import json
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BlockchainDaemon:
|
|
10
|
+
"""
|
|
11
|
+
Primary blockchain daemon that manages the authoritative blockchain state.
|
|
12
|
+
Validates all transactions, manages peer registry, and serves as source of truth.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, blockchain_manager, mempool_manager, security_manager=None):
|
|
16
|
+
self.blockchain = blockchain_manager
|
|
17
|
+
self.mempool = mempool_manager
|
|
18
|
+
self.security = security_manager
|
|
19
|
+
|
|
20
|
+
# Initialize difficulty system for validation
|
|
21
|
+
from ..mining.difficulty import DifficultySystem
|
|
22
|
+
self.difficulty_system = DifficultySystem()
|
|
23
|
+
|
|
24
|
+
# Peer registry
|
|
25
|
+
self.peers = {} # {node_id: peer_info}
|
|
26
|
+
self.peer_lock = threading.Lock()
|
|
27
|
+
|
|
28
|
+
# Daemon state
|
|
29
|
+
self.is_running = False
|
|
30
|
+
self.validation_thread = None
|
|
31
|
+
self.cleanup_thread = None
|
|
32
|
+
|
|
33
|
+
# Statistics
|
|
34
|
+
self.stats = {
|
|
35
|
+
'blocks_validated': 0,
|
|
36
|
+
'transactions_validated': 0,
|
|
37
|
+
'peers_registered': 0,
|
|
38
|
+
'start_time': time.time()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def start(self):
|
|
42
|
+
"""Start the blockchain daemon"""
|
|
43
|
+
if self.is_running:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
self.is_running = True
|
|
47
|
+
print("🏛️ Blockchain Daemon starting...")
|
|
48
|
+
|
|
49
|
+
# Start background threads
|
|
50
|
+
self.validation_thread = threading.Thread(target=self._validation_loop, daemon=True)
|
|
51
|
+
self.validation_thread.start()
|
|
52
|
+
|
|
53
|
+
self.cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True)
|
|
54
|
+
self.cleanup_thread.start()
|
|
55
|
+
|
|
56
|
+
print("✅ Blockchain Daemon started")
|
|
57
|
+
|
|
58
|
+
def stop(self):
|
|
59
|
+
"""Stop the blockchain daemon"""
|
|
60
|
+
self.is_running = False
|
|
61
|
+
|
|
62
|
+
if self.validation_thread:
|
|
63
|
+
self.validation_thread.join(timeout=5)
|
|
64
|
+
if self.cleanup_thread:
|
|
65
|
+
self.cleanup_thread.join(timeout=5)
|
|
66
|
+
|
|
67
|
+
print("🛑 Blockchain Daemon stopped")
|
|
68
|
+
|
|
69
|
+
def register_peer(self, peer_info: Dict) -> Dict:
|
|
70
|
+
"""
|
|
71
|
+
Register a new peer node.
|
|
72
|
+
Returns: {'success': bool, 'node_id': str, 'message': str}
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
node_id = peer_info.get('node_id')
|
|
76
|
+
if not node_id:
|
|
77
|
+
return {'success': False, 'message': 'Missing node_id'}
|
|
78
|
+
|
|
79
|
+
with self.peer_lock:
|
|
80
|
+
self.peers[node_id] = {
|
|
81
|
+
'node_id': node_id,
|
|
82
|
+
'registered_at': time.time(),
|
|
83
|
+
'last_seen': time.time(),
|
|
84
|
+
'capabilities': peer_info.get('capabilities', []),
|
|
85
|
+
'url': peer_info.get('url'),
|
|
86
|
+
'version': peer_info.get('version', 'unknown')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
self.stats['peers_registered'] += 1
|
|
90
|
+
|
|
91
|
+
print(f"👤 New peer registered: {node_id}")
|
|
92
|
+
return {
|
|
93
|
+
'success': True,
|
|
94
|
+
'node_id': node_id,
|
|
95
|
+
'message': 'Peer registered successfully'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
except Exception as e:
|
|
99
|
+
return {'success': False, 'message': str(e)}
|
|
100
|
+
|
|
101
|
+
def unregister_peer(self, node_id: str) -> Dict:
|
|
102
|
+
"""
|
|
103
|
+
Unregister a peer node.
|
|
104
|
+
Returns: {'success': bool, 'message': str}
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
with self.peer_lock:
|
|
108
|
+
if node_id in self.peers:
|
|
109
|
+
del self.peers[node_id]
|
|
110
|
+
print(f"👋 Peer unregistered: {node_id}")
|
|
111
|
+
return {'success': True, 'message': 'Peer unregistered'}
|
|
112
|
+
else:
|
|
113
|
+
return {'success': False, 'message': 'Peer not found'}
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return {'success': False, 'message': str(e)}
|
|
116
|
+
|
|
117
|
+
def get_peer_list(self, exclude_node_id: Optional[str] = None) -> List[Dict]:
|
|
118
|
+
"""
|
|
119
|
+
Get list of registered peers.
|
|
120
|
+
exclude_node_id: Optional node ID to exclude from results
|
|
121
|
+
"""
|
|
122
|
+
with self.peer_lock:
|
|
123
|
+
peer_list = []
|
|
124
|
+
for node_id, peer_info in self.peers.items():
|
|
125
|
+
if exclude_node_id and node_id == exclude_node_id:
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
peer_list.append({
|
|
129
|
+
'node_id': peer_info['node_id'],
|
|
130
|
+
'url': peer_info.get('url'),
|
|
131
|
+
'last_seen': peer_info['last_seen'],
|
|
132
|
+
'capabilities': peer_info.get('capabilities', [])
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return peer_list
|
|
136
|
+
|
|
137
|
+
def update_peer_heartbeat(self, node_id: str):
|
|
138
|
+
"""Update peer's last seen timestamp"""
|
|
139
|
+
with self.peer_lock:
|
|
140
|
+
if node_id in self.peers:
|
|
141
|
+
self.peers[node_id]['last_seen'] = time.time()
|
|
142
|
+
|
|
143
|
+
def validate_block(self, block: Dict) -> Dict:
|
|
144
|
+
"""
|
|
145
|
+
Validate a block submitted by a peer or miner.
|
|
146
|
+
Returns: {'valid': bool, 'message': str, 'errors': List[str]}
|
|
147
|
+
"""
|
|
148
|
+
errors = []
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
# Basic structure validation using difficulty system
|
|
152
|
+
is_valid, error_msg = self.difficulty_system.validate_block_structure(block)
|
|
153
|
+
if not is_valid:
|
|
154
|
+
errors.append(error_msg)
|
|
155
|
+
return {'valid': False, 'message': 'Invalid block structure', 'errors': errors}
|
|
156
|
+
|
|
157
|
+
# Validate block index
|
|
158
|
+
latest_block = self.blockchain.get_latest_block()
|
|
159
|
+
if latest_block:
|
|
160
|
+
expected_index = latest_block.get('index', 0) + 1
|
|
161
|
+
if block['index'] != expected_index:
|
|
162
|
+
errors.append(f"Invalid block index: expected {expected_index}, got {block['index']}")
|
|
163
|
+
|
|
164
|
+
# Validate previous hash
|
|
165
|
+
if latest_block and block['previous_hash'] != latest_block.get('hash'):
|
|
166
|
+
expected_hash = latest_block.get('hash', '')
|
|
167
|
+
actual_hash = block.get('previous_hash', '')
|
|
168
|
+
errors.append(f"Previous hash mismatch: expected {expected_hash[:16]}..., got {actual_hash[:16]}...")
|
|
169
|
+
|
|
170
|
+
# Validate hash meets difficulty requirement using difficulty system
|
|
171
|
+
difficulty = block.get('difficulty', 0)
|
|
172
|
+
block_hash = block.get('hash', '')
|
|
173
|
+
|
|
174
|
+
if not self.difficulty_system.validate_block_hash(block_hash, difficulty):
|
|
175
|
+
errors.append(f"Hash doesn't meet difficulty {difficulty} requirement (needs {difficulty} leading zeros)")
|
|
176
|
+
|
|
177
|
+
# Validate exponential reward matches difficulty
|
|
178
|
+
expected_reward = self.difficulty_system.calculate_block_reward(difficulty)
|
|
179
|
+
actual_reward = block.get('reward', 0)
|
|
180
|
+
|
|
181
|
+
# Allow small tolerance for floating point comparison
|
|
182
|
+
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")
|
|
184
|
+
|
|
185
|
+
# Validate transactions
|
|
186
|
+
for tx in block.get('transactions', []):
|
|
187
|
+
tx_validation = self.validate_transaction(tx)
|
|
188
|
+
if not tx_validation['valid']:
|
|
189
|
+
errors.append(f"Invalid transaction: {tx_validation['message']}")
|
|
190
|
+
|
|
191
|
+
if errors:
|
|
192
|
+
return {'valid': False, 'message': 'Block validation failed', 'errors': errors}
|
|
193
|
+
|
|
194
|
+
self.stats['blocks_validated'] += 1
|
|
195
|
+
print(f"✅ Block #{block['index']} validated: Difficulty {difficulty}, Reward {actual_reward} LKC, Hash {block_hash[:16]}...")
|
|
196
|
+
return {'valid': True, 'message': 'Block is valid', 'errors': []}
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
return {'valid': False, 'message': f'Validation error: {str(e)}', 'errors': [str(e)]}
|
|
200
|
+
|
|
201
|
+
def validate_transaction(self, transaction: Dict) -> Dict:
|
|
202
|
+
"""
|
|
203
|
+
Validate a transaction submitted by a peer.
|
|
204
|
+
Returns: {'valid': bool, 'message': str, 'errors': List[str]}
|
|
205
|
+
"""
|
|
206
|
+
errors = []
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
# Basic validation
|
|
210
|
+
tx_type = transaction.get('type')
|
|
211
|
+
if not tx_type:
|
|
212
|
+
errors.append("Missing transaction type")
|
|
213
|
+
|
|
214
|
+
# Type-specific validation
|
|
215
|
+
if tx_type == 'transaction':
|
|
216
|
+
required = ['from', 'to', 'amount', 'timestamp']
|
|
217
|
+
for field in required:
|
|
218
|
+
if field not in transaction:
|
|
219
|
+
errors.append(f"Missing field: {field}")
|
|
220
|
+
|
|
221
|
+
# Validate amount
|
|
222
|
+
amount = transaction.get('amount', 0)
|
|
223
|
+
if amount <= 0:
|
|
224
|
+
errors.append("Invalid amount: must be positive")
|
|
225
|
+
|
|
226
|
+
elif tx_type == 'genesis_bill':
|
|
227
|
+
if 'denomination' not in transaction:
|
|
228
|
+
errors.append("Missing denomination for genesis bill")
|
|
229
|
+
|
|
230
|
+
elif tx_type == 'reward':
|
|
231
|
+
required = ['to', 'amount']
|
|
232
|
+
for field in required:
|
|
233
|
+
if field not in transaction:
|
|
234
|
+
errors.append(f"Missing field: {field}")
|
|
235
|
+
|
|
236
|
+
# Security validation if available
|
|
237
|
+
if self.security and not errors:
|
|
238
|
+
try:
|
|
239
|
+
# Use security manager for advanced validation
|
|
240
|
+
# security_result = self.security.validate_transaction(transaction)
|
|
241
|
+
pass
|
|
242
|
+
except Exception as e:
|
|
243
|
+
errors.append(f"Security validation error: {e}")
|
|
244
|
+
|
|
245
|
+
if errors:
|
|
246
|
+
return {'valid': False, 'message': 'Transaction validation failed', 'errors': errors}
|
|
247
|
+
|
|
248
|
+
self.stats['transactions_validated'] += 1
|
|
249
|
+
return {'valid': True, 'message': 'Transaction is valid', 'errors': []}
|
|
250
|
+
|
|
251
|
+
except Exception as e:
|
|
252
|
+
return {'valid': False, 'message': f'Validation error: {str(e)}', 'errors': [str(e)]}
|
|
253
|
+
|
|
254
|
+
def process_incoming_block(self, block: Dict, from_peer: Optional[str] = None) -> Dict:
|
|
255
|
+
"""
|
|
256
|
+
Process an incoming block from P2P network.
|
|
257
|
+
Validates and adds to blockchain if valid.
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
# Validate block
|
|
261
|
+
validation = self.validate_block(block)
|
|
262
|
+
|
|
263
|
+
if not validation['valid']:
|
|
264
|
+
print(f"❌ Invalid block from peer {from_peer}: {validation['message']}")
|
|
265
|
+
return validation
|
|
266
|
+
|
|
267
|
+
# Add to blockchain
|
|
268
|
+
success = self.blockchain.submit_mined_block(block)
|
|
269
|
+
|
|
270
|
+
if success:
|
|
271
|
+
print(f"✅ Block #{block['index']} accepted from peer {from_peer}")
|
|
272
|
+
|
|
273
|
+
# Broadcast to other peers
|
|
274
|
+
self._broadcast_block_to_peers(block, exclude=from_peer)
|
|
275
|
+
|
|
276
|
+
return {'success': True, 'message': 'Block accepted and propagated'}
|
|
277
|
+
else:
|
|
278
|
+
return {'success': False, 'message': 'Block submission failed'}
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
return {'success': False, 'message': f'Processing error: {str(e)}'}
|
|
282
|
+
|
|
283
|
+
def process_incoming_transaction(self, transaction: Dict, from_peer: Optional[str] = None) -> Dict:
|
|
284
|
+
"""
|
|
285
|
+
Process an incoming transaction from P2P network.
|
|
286
|
+
Validates and adds to mempool if valid.
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
# Validate transaction
|
|
290
|
+
validation = self.validate_transaction(transaction)
|
|
291
|
+
|
|
292
|
+
if not validation['valid']:
|
|
293
|
+
print(f"❌ Invalid transaction from peer {from_peer}: {validation['message']}")
|
|
294
|
+
return validation
|
|
295
|
+
|
|
296
|
+
# Add to mempool
|
|
297
|
+
# self.mempool.add_transaction(transaction)
|
|
298
|
+
print(f"✅ Transaction accepted from peer {from_peer}")
|
|
299
|
+
|
|
300
|
+
# Broadcast to other peers
|
|
301
|
+
self._broadcast_transaction_to_peers(transaction, exclude=from_peer)
|
|
302
|
+
|
|
303
|
+
return {'success': True, 'message': 'Transaction accepted and propagated'}
|
|
304
|
+
|
|
305
|
+
except Exception as e:
|
|
306
|
+
return {'success': False, 'message': f'Processing error: {str(e)}'}
|
|
307
|
+
|
|
308
|
+
def _broadcast_block_to_peers(self, block: Dict, exclude: Optional[str] = None):
|
|
309
|
+
"""Broadcast block to all registered peers except excluded one"""
|
|
310
|
+
with self.peer_lock:
|
|
311
|
+
for node_id, peer_info in self.peers.items():
|
|
312
|
+
if node_id == exclude:
|
|
313
|
+
continue
|
|
314
|
+
|
|
315
|
+
# Send block to peer (implementation depends on transport)
|
|
316
|
+
# This would typically use HTTP POST or WebSocket
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
def _broadcast_transaction_to_peers(self, transaction: Dict, exclude: Optional[str] = None):
|
|
320
|
+
"""Broadcast transaction to all registered peers except excluded one"""
|
|
321
|
+
with self.peer_lock:
|
|
322
|
+
for node_id, peer_info in self.peers.items():
|
|
323
|
+
if node_id == exclude:
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
# Send transaction to peer
|
|
327
|
+
pass
|
|
328
|
+
|
|
329
|
+
def _validation_loop(self):
|
|
330
|
+
"""Background thread for continuous validation"""
|
|
331
|
+
while self.is_running:
|
|
332
|
+
try:
|
|
333
|
+
# Periodic mempool validation
|
|
334
|
+
# Remove invalid transactions from mempool
|
|
335
|
+
time.sleep(30)
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
print(f"❌ Validation loop error: {e}")
|
|
339
|
+
time.sleep(10)
|
|
340
|
+
|
|
341
|
+
def _cleanup_loop(self):
|
|
342
|
+
"""Background thread for peer cleanup"""
|
|
343
|
+
while self.is_running:
|
|
344
|
+
try:
|
|
345
|
+
current_time = time.time()
|
|
346
|
+
timeout = 300 # 5 minutes
|
|
347
|
+
|
|
348
|
+
with self.peer_lock:
|
|
349
|
+
inactive_peers = []
|
|
350
|
+
for node_id, peer_info in self.peers.items():
|
|
351
|
+
if current_time - peer_info['last_seen'] > timeout:
|
|
352
|
+
inactive_peers.append(node_id)
|
|
353
|
+
|
|
354
|
+
# Remove inactive peers
|
|
355
|
+
for node_id in inactive_peers:
|
|
356
|
+
del self.peers[node_id]
|
|
357
|
+
print(f"🧹 Removed inactive peer: {node_id}")
|
|
358
|
+
|
|
359
|
+
time.sleep(60) # Check every minute
|
|
360
|
+
|
|
361
|
+
except Exception as e:
|
|
362
|
+
print(f"❌ Cleanup loop error: {e}")
|
|
363
|
+
time.sleep(60)
|
|
364
|
+
|
|
365
|
+
def get_stats(self) -> Dict:
|
|
366
|
+
"""Get daemon statistics"""
|
|
367
|
+
uptime = time.time() - self.stats['start_time']
|
|
368
|
+
|
|
369
|
+
with self.peer_lock:
|
|
370
|
+
peer_count = len(self.peers)
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
'uptime_seconds': uptime,
|
|
374
|
+
'blocks_validated': self.stats['blocks_validated'],
|
|
375
|
+
'transactions_validated': self.stats['transactions_validated'],
|
|
376
|
+
'peers_registered': self.stats['peers_registered'],
|
|
377
|
+
'active_peers': peer_count,
|
|
378
|
+
'mempool_size': len(self.mempool.get_pending_transactions()) if self.mempool else 0
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
def get_blockchain_state(self) -> Dict:
|
|
382
|
+
"""Get current blockchain state for peers"""
|
|
383
|
+
latest_block = self.blockchain.get_latest_block()
|
|
384
|
+
height = self.blockchain.get_blockchain_height()
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
'height': height,
|
|
388
|
+
'latest_block': latest_block,
|
|
389
|
+
'mempool_size': len(self.mempool.get_pending_transactions()) if self.mempool else 0,
|
|
390
|
+
'timestamp': time.time()
|
|
391
|
+
}
|