lunalib 1.1.0__py3-none-any.whl → 1.5.0__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/mining/miner.py CHANGED
@@ -1,55 +1,205 @@
1
+ # lunalib/mining/miner.py
1
2
  import time
2
3
  import hashlib
3
4
  import json
4
5
  import threading
5
- from typing import Dict, Optional
6
- from lunalib.mining.difficulty import DifficultySystem
7
- from lunalib.gtx.digital_bill import DigitalBill
6
+ from typing import Dict, Optional, List, Union, Callable
7
+ from ..mining.difficulty import DifficultySystem
8
+ from ..gtx.digital_bill import DigitalBill
9
+ from ..transactions.transactions import TransactionManager
10
+ from ..core.blockchain import BlockchainManager
11
+ from ..core.mempool import MempoolManager
8
12
 
9
13
  class GenesisMiner:
10
- """Mines GTX Genesis bills with configurable difficulty"""
14
+ """Mines GTX Genesis bills AND regular transfer transactions with configurable difficulty"""
11
15
 
12
- def __init__(self):
16
+ def __init__(self, network_endpoints: List[str] = None):
13
17
  self.difficulty_system = DifficultySystem()
18
+ self.transaction_manager = TransactionManager(network_endpoints)
19
+ self.blockchain_manager = BlockchainManager(network_endpoints[0] if network_endpoints else "https://bank.linglin.art")
20
+ self.mempool_manager = MempoolManager(network_endpoints)
21
+
14
22
  self.mining_active = False
15
23
  self.current_thread = None
24
+ self.mining_stats = {
25
+ "bills_mined": 0,
26
+ "blocks_mined": 0,
27
+ "total_mining_time": 0,
28
+ "total_hash_attempts": 0
29
+ }
16
30
 
17
- def mine_bill(self, denomination, user_address, bill_data=None):
18
- """Mine a GTX Genesis bill"""
19
- difficulty = self.difficulty_system.get_bill_difficulty(denomination)
20
-
21
- digital_bill = DigitalBill(
22
- denomination=denomination,
23
- user_address=user_address,
24
- difficulty=difficulty,
25
- bill_data=bill_data or {}
26
- )
27
-
28
- print(f"⛏️ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
29
-
30
- start_time = time.time()
31
- mining_result = self._perform_mining(digital_bill, difficulty)
32
-
33
- if mining_result["success"]:
34
- mining_time = time.time() - start_time
35
- bill = digital_bill.finalize(
36
- hash=mining_result["hash"],
37
- nonce=mining_result["nonce"],
38
- mining_time=mining_time
31
+ print("🔧 GenesisMiner initialized with integrated lunalib components")
32
+
33
+ def mine_bill(self, denomination: float, user_address: str, bill_data: Dict = None) -> Dict:
34
+ """Mine a GTX Genesis bill using DigitalBill system"""
35
+ try:
36
+ difficulty = self.difficulty_system.get_bill_difficulty(denomination)
37
+
38
+ # Create digital bill using GTX system
39
+ digital_bill = DigitalBill(
40
+ denomination=denomination,
41
+ user_address=user_address,
42
+ difficulty=difficulty,
43
+ bill_data=bill_data or {}
39
44
  )
40
45
 
41
- print(f" Successfully mined GTX ${denomination:,} bill!")
42
- print(f"⏱️ Mining time: {mining_time:.2f}s")
46
+ print(f"⛏️ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
47
+
48
+ start_time = time.time()
49
+ mining_result = self._perform_bill_mining(digital_bill, difficulty)
43
50
 
44
- return bill
45
- else:
46
- return {"success": False, "error": "Mining failed"}
51
+ if mining_result["success"]:
52
+ mining_time = time.time() - start_time
53
+
54
+ # Finalize the bill
55
+ bill = digital_bill.finalize(
56
+ hash=mining_result["hash"],
57
+ nonce=mining_result["nonce"],
58
+ mining_time=mining_time
59
+ )
60
+
61
+ # Update mining statistics
62
+ self.mining_stats["bills_mined"] += 1
63
+ self.mining_stats["total_mining_time"] += mining_time
64
+ self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
65
+
66
+ print(f"✅ Successfully mined GTX ${denomination:,} bill!")
67
+ print(f"⏱️ Mining time: {mining_time:.2f}s")
68
+ print(f"📊 Hash attempts: {mining_result['nonce']:,}")
69
+ print(f"🔗 Bill hash: {mining_result['hash'][:32]}...")
70
+
71
+ # Convert to GTX Genesis transaction
72
+ gtx_transaction = self._create_gtx_genesis_transaction(bill)
73
+ return {
74
+ "success": True,
75
+ "type": "bill",
76
+ "bill": bill,
77
+ "transaction": gtx_transaction,
78
+ "mining_time": mining_time,
79
+ "hash_attempts": mining_result["nonce"]
80
+ }
81
+ else:
82
+ return {"success": False, "error": "Bill mining failed"}
83
+
84
+ except Exception as e:
85
+ print(f"[X]Error mining bill: {e}")
86
+ return {"success": False, "error": str(e)}
87
+
88
+ def mine_transaction_block(self, miner_address: str, previous_hash: str = None, block_height: int = None) -> Dict:
89
+ """Mine a block containing transactions from mempool"""
90
+ try:
91
+ # Get current blockchain state if not provided
92
+ if previous_hash is None or block_height is None:
93
+ current_height = self.blockchain_manager.get_blockchain_height()
94
+ latest_block = self.blockchain_manager.get_latest_block()
95
+ block_height = current_height + 1
96
+ previous_hash = latest_block.get('hash', '0' * 64) if latest_block else '0' * 64
97
+
98
+ # Get transactions from mempool
99
+ pending_txs = self.mempool_manager.get_pending_transactions()
100
+ transactions = pending_txs[:10] # Limit block size
101
+
102
+ if not transactions:
103
+ return {"success": False, "error": "No transactions in mempool"}
104
+
105
+ # Calculate block difficulty
106
+ difficulty = self.difficulty_system.get_transaction_block_difficulty(transactions)
107
+
108
+ print(f"⛏️ Mining Transaction Block #{block_height} - Difficulty: {difficulty} zeros")
109
+ print(f"📦 Transactions: {len(transactions)} | Previous Hash: {previous_hash[:16]}...")
110
+
111
+ # Create block structure for mining
112
+ block_data = {
113
+ "index": block_height,
114
+ "previous_hash": previous_hash,
115
+ "timestamp": time.time(),
116
+ "transactions": transactions,
117
+ "miner": miner_address,
118
+ "difficulty": difficulty,
119
+ "nonce": 0,
120
+ "version": "1.0"
121
+ }
122
+
123
+ start_time = time.time()
124
+ mining_result = self._perform_block_mining(block_data, difficulty)
125
+
126
+ if mining_result["success"]:
127
+ mining_time = time.time() - start_time
128
+
129
+ # Add hash and nonce to block_data for validation
130
+ block_data["hash"] = mining_result["hash"]
131
+ block_data["nonce"] = mining_result["nonce"]
132
+
133
+ # Create reward transaction WITH VALIDATION
134
+ reward_tx = self._create_mining_reward_transaction(
135
+ miner_address=miner_address,
136
+ block_height=block_height,
137
+ transactions=transactions,
138
+ block_data=block_data # Pass block data for validation
139
+ )
140
+
141
+ # Add reward transaction
142
+ block_data["transactions"].append(reward_tx)
143
+
144
+ # Calculate merkleroot for submission
145
+ merkleroot = self._calculate_merkleroot(transactions) # Without reward
146
+
147
+ # Finalize block
148
+ block = {
149
+ **block_data,
150
+ "hash": mining_result["hash"],
151
+ "nonce": mining_result["nonce"],
152
+ "merkleroot": merkleroot,
153
+ "transactions_hash": merkleroot,
154
+ "mining_time": mining_time,
155
+ "reward": reward_tx["amount"],
156
+ "transaction_count": len(block_data["transactions"]),
157
+ "timestamp": block_data["timestamp"] # Ensure timestamp is included
158
+ }
159
+
160
+ # Update mining statistics
161
+ self.mining_stats["blocks_mined"] += 1
162
+ self.mining_stats["total_mining_time"] += mining_time
163
+ self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
164
+
165
+ print(f"✅ Successfully mined and validated Transaction Block #{block_height}!")
166
+ print(f"⏱️ Mining time: {mining_time:.2f}s")
167
+ print(f"💰 Block reward: {block['reward']:.6f} LUN")
168
+ print(f"📊 Transactions: {block['transaction_count']}")
169
+ print(f"🔗 Block hash: {mining_result['hash'][:32]}...")
170
+
171
+ # Submit block to blockchain
172
+ submission_success = self.blockchain_manager.submit_mined_block(block)
173
+ if submission_success:
174
+ print("✅ Block successfully submitted to blockchain!")
175
+ # Clear mined transactions from local mempool
176
+ self._clear_mined_transactions(transactions)
177
+ else:
178
+ print("⚠️ Block mined but submission failed")
179
+
180
+ return {
181
+ "success": True,
182
+ "type": "block",
183
+ "block": block,
184
+ "submitted": submission_success,
185
+ "mining_time": mining_time,
186
+ "hash_attempts": mining_result["nonce"]
187
+ }
188
+ else:
189
+ return {"success": False, "error": "Block mining failed"}
190
+
191
+ except Exception as e:
192
+ print(f"❌ Error mining block: {e}")
193
+ import traceback
194
+ traceback.print_exc()
195
+ return {"success": False, "error": str(e)}
47
196
 
48
- def _perform_mining(self, digital_bill, difficulty):
49
- """Perform proof-of-work mining"""
197
+ def _perform_bill_mining(self, digital_bill: DigitalBill, difficulty: int) -> Dict:
198
+ """Perform proof-of-work mining for GTX bills"""
50
199
  target = "0" * difficulty
51
200
  nonce = 0
52
201
  start_time = time.time()
202
+ last_update = start_time
53
203
 
54
204
  while self.mining_active:
55
205
  mining_data = digital_bill.get_mining_data(nonce)
@@ -67,16 +217,211 @@ class GenesisMiner:
67
217
 
68
218
  nonce += 1
69
219
 
70
- # Progress updates
71
- if nonce % 100000 == 0:
72
- current_time = time.time()
220
+ # Progress updates every 5 seconds
221
+ current_time = time.time()
222
+ if current_time - last_update >= 5:
73
223
  hashrate = nonce / (current_time - start_time)
74
- print(f"⏳ Attempts: {nonce:,} | Rate: {hashrate:,.0f} H/s")
224
+ print(f"⏳ Bill mining: {nonce:,} attempts | Rate: {hashrate:,.0f} H/s")
225
+ last_update = current_time
75
226
 
76
227
  return {"success": False, "error": "Mining stopped"}
77
228
 
78
- def start_auto_mining(self, denominations, user_address, callback=None):
79
- """Start auto-mining multiple bills"""
229
+ def _perform_block_mining(self, block_data: Dict, difficulty: int) -> Dict:
230
+ """Perform proof-of-work mining for transaction blocks"""
231
+ target = "0" * difficulty
232
+ nonce = 0
233
+ start_time = time.time()
234
+ last_update = start_time
235
+
236
+ while self.mining_active:
237
+ # Update nonce for this attempt
238
+ block_data["nonce"] = nonce
239
+
240
+ # Create block hash
241
+ block_string = json.dumps(block_data, sort_keys=True)
242
+ block_hash = hashlib.sha256(block_string.encode()).hexdigest()
243
+
244
+ if block_hash.startswith(target):
245
+ mining_time = time.time() - start_time
246
+ return {
247
+ "success": True,
248
+ "hash": block_hash,
249
+ "nonce": nonce,
250
+ "mining_time": mining_time
251
+ }
252
+
253
+ nonce += 1
254
+
255
+ # Progress updates every 5 seconds
256
+ current_time = time.time()
257
+ if current_time - last_update >= 5:
258
+ hashrate = nonce / (current_time - start_time)
259
+ print(f"Block mining: {nonce:,} attempts | Rate: {hashrate:,.0f} H/s")
260
+ last_update = current_time
261
+
262
+ return {"success": False, "error": "Mining stopped"}
263
+
264
+ def _create_gtx_genesis_transaction(self, bill: Dict) -> Dict:
265
+ """Create GTX Genesis transaction from mined bill"""
266
+ return self.transaction_manager.create_gtx_transaction(bill)
267
+
268
+ def _create_mining_reward_transaction(self, miner_address: str, block_height: int,
269
+ transactions: List[Dict], block_data: Dict = None) -> Dict:
270
+ """Create mining reward transaction with validation of the mining proof"""
271
+
272
+ # Calculate reward
273
+ base_reward = 1.0 # Base block reward
274
+ total_fees = sum(tx.get('fee', 0) for tx in transactions)
275
+ total_reward = base_reward + total_fees
276
+
277
+ # If block_data is provided, validate the mining proof
278
+ if block_data:
279
+ print("🔍 Validating mining proof before creating reward...")
280
+
281
+ # Extract mining proof components
282
+ block_hash = block_data.get('hash', '')
283
+ difficulty = block_data.get('difficulty', 0)
284
+ nonce = block_data.get('nonce', 0)
285
+ timestamp = block_data.get('timestamp', time.time())
286
+ previous_hash = block_data.get('previous_hash', '0' * 64)
287
+ miner = block_data.get('miner', miner_address)
288
+
289
+ # Calculate merkleroot from transactions
290
+ merkleroot = self._calculate_merkleroot(transactions)
291
+
292
+ print(f"📊 Mining proof components:")
293
+ print(f" Block hash: {block_hash[:16]}...")
294
+ print(f" Difficulty: {difficulty}")
295
+ print(f" Nonce: {nonce}")
296
+ print(f" Timestamp: {timestamp}")
297
+ print(f" Previous hash: {previous_hash[:16]}...")
298
+ print(f" Miner: {miner}")
299
+ print(f" Merkleroot: {merkleroot[:16]}...")
300
+
301
+ # Validate difficulty requirement
302
+ if not block_hash.startswith('0' * difficulty):
303
+ print(f"❌ FAIL: Hash doesn't start with {difficulty} zeros")
304
+ raise ValueError(f"Invalid mining proof: Hash doesn't meet difficulty requirement")
305
+
306
+ # Try multiple validation methods
307
+ validation_passed = False
308
+
309
+ # Method 1: Original format (what server likely expects)
310
+ original_string = f"{previous_hash}{timestamp}{merkleroot}{miner}{nonce}"
311
+ original_hash = hashlib.sha256(original_string.encode()).hexdigest()
312
+
313
+ print(f"🔍 Original format validation:")
314
+ print(f" String: {original_string[:80]}...")
315
+ print(f" Calculated: {original_hash[:16]}...")
316
+
317
+ if original_hash == block_hash:
318
+ validation_passed = True
319
+ print("✅ Original format validation passed")
320
+
321
+ # Method 2: JSON format (what miner might be using)
322
+ if not validation_passed:
323
+ mining_json = {
324
+ "index": block_height,
325
+ "previous_hash": previous_hash,
326
+ "timestamp": timestamp,
327
+ "transactions": transactions,
328
+ "miner": miner,
329
+ "difficulty": difficulty,
330
+ "nonce": nonce,
331
+ "version": "1.0"
332
+ }
333
+
334
+ json_string = json.dumps(mining_json, sort_keys=True)
335
+ json_hash = hashlib.sha256(json_string.encode()).hexdigest()
336
+
337
+ print(f"🔍 JSON format validation:")
338
+ print(f" String: {json_string[:100]}...")
339
+ print(f" Calculated: {json_hash[:16]}...")
340
+
341
+ if json_hash == block_hash:
342
+ validation_passed = True
343
+ print("✅ JSON format validation passed")
344
+
345
+ # Method 3: JSON without transactions (for empty blocks)
346
+ if not validation_passed and len(transactions) == 0:
347
+ mining_json_empty = {
348
+ "index": block_height,
349
+ "previous_hash": previous_hash,
350
+ "timestamp": timestamp,
351
+ "transactions": [],
352
+ "miner": miner,
353
+ "difficulty": difficulty,
354
+ "nonce": nonce,
355
+ "version": "1.0"
356
+ }
357
+
358
+ json_string_empty = json.dumps(mining_json_empty, sort_keys=True)
359
+ json_hash_empty = hashlib.sha256(json_string_empty.encode()).hexdigest()
360
+
361
+ print(f"🔍 JSON empty format validation:")
362
+ print(f" Calculated: {json_hash_empty[:16]}...")
363
+
364
+ if json_hash_empty == block_hash:
365
+ validation_passed = True
366
+ print("✅ JSON empty format validation passed")
367
+
368
+ if not validation_passed:
369
+ print("❌ All validation methods failed")
370
+ raise ValueError("Invalid mining proof: Hash verification failed")
371
+
372
+ print("✅ Mining proof validation successful!")
373
+
374
+ # Create the reward transaction
375
+ return self.transaction_manager.create_reward_transaction(
376
+ to_address=miner_address,
377
+ amount=total_reward,
378
+ block_height=block_height,
379
+ reward_type="block"
380
+ )
381
+
382
+ def _calculate_merkleroot(self, transactions: List[Dict]) -> str:
383
+ """Calculate merkle root from transactions"""
384
+ if not transactions:
385
+ return "0" * 64
386
+
387
+ tx_hashes = []
388
+ for tx in transactions:
389
+ if 'hash' in tx:
390
+ tx_hashes.append(tx['hash'])
391
+ else:
392
+ tx_string = json.dumps(tx, sort_keys=True)
393
+ tx_hashes.append(hashlib.sha256(tx_string.encode()).hexdigest())
394
+
395
+ # Simple merkle root calculation
396
+ while len(tx_hashes) > 1:
397
+ new_hashes = []
398
+ for i in range(0, len(tx_hashes), 2):
399
+ if i + 1 < len(tx_hashes):
400
+ combined = tx_hashes[i] + tx_hashes[i + 1]
401
+ else:
402
+ combined = tx_hashes[i] + tx_hashes[i]
403
+ new_hash = hashlib.sha256(combined.encode()).hexdigest()
404
+ new_hashes.append(new_hash)
405
+ tx_hashes = new_hashes
406
+
407
+ return tx_hashes[0] if tx_hashes else "0" * 64
408
+
409
+ def _clear_mined_transactions(self, mined_transactions: List[Dict]):
410
+ """Remove mined transactions from local mempool"""
411
+ for tx in mined_transactions:
412
+ tx_hash = tx.get('hash')
413
+ if tx_hash:
414
+ self.mempool_manager.remove_transaction(tx_hash)
415
+
416
+ print(f"Cleared {len(mined_transactions)} mined transactions from mempool")
417
+
418
+ def start_auto_bill_mining(self, denominations: List[float], user_address: str,
419
+ callback: Callable = None) -> bool:
420
+ """Start auto-mining multiple GTX bills"""
421
+ if self.mining_active:
422
+ print("Mining already active")
423
+ return False
424
+
80
425
  self.mining_active = True
81
426
 
82
427
  def auto_mine():
@@ -85,23 +430,188 @@ class GenesisMiner:
85
430
  if not self.mining_active:
86
431
  break
87
432
 
433
+ print(f"Starting auto-mining for ${denomination:,} bill...")
88
434
  result = self.mine_bill(denomination, user_address)
89
435
  results.append(result)
90
436
 
91
437
  if callback:
92
438
  callback(result)
93
439
 
440
+ # Brief pause between bills
94
441
  time.sleep(1)
95
442
 
443
+ print("Auto bill mining completed")
96
444
  return results
97
445
 
98
446
  self.current_thread = threading.Thread(target=auto_mine, daemon=True)
99
447
  self.current_thread.start()
448
+ print(f"Started auto-mining {len(denominations)} bills")
449
+ return True
450
+
451
+ def start_continuous_block_mining(self, miner_address: str, callback: Callable = None) -> bool:
452
+ """Start continuous transaction block mining"""
453
+ if self.mining_active:
454
+ print("⚠️ Mining already active")
455
+ return False
456
+
457
+ self.mining_active = True
458
+
459
+ def continuous_mine():
460
+ block_height = self.blockchain_manager.get_blockchain_height() + 1
461
+ latest_block = self.blockchain_manager.get_latest_block()
462
+ previous_hash = latest_block.get('hash', '0' * 64) if latest_block else '0' * 64
463
+
464
+ while self.mining_active:
465
+ # Check mempool for transactions
466
+ pending_count = len(self.mempool_manager.get_pending_transactions())
467
+
468
+ if pending_count > 0:
469
+ print(f"🔄 Mining block #{block_height} with {pending_count} pending transactions...")
470
+
471
+ result = self.mine_transaction_block(miner_address, previous_hash, block_height)
472
+
473
+ if result.get("success"):
474
+ if callback:
475
+ callback(result)
476
+
477
+ # Update for next block
478
+ block_height += 1
479
+ previous_hash = result["block"]["hash"]
480
+
481
+ # Brief pause between blocks
482
+ time.sleep(2)
483
+ else:
484
+ print("⏳ No transactions in mempool, waiting...")
485
+ time.sleep(10) # Wait longer if no transactions
486
+
487
+ self.current_thread = threading.Thread(target=continuous_mine, daemon=True)
488
+ self.current_thread.start()
489
+ print("Started continuous block mining")
100
490
  return True
101
491
 
492
+ def start_hybrid_mining(self, miner_address: str, bill_denominations: List[float] = None,
493
+ callback: Callable = None) -> bool:
494
+ """Start hybrid mining - both GTX bills and transaction blocks"""
495
+ if self.mining_active:
496
+ print("Mining already active")
497
+ return False
498
+
499
+ self.mining_active = True
500
+
501
+ def hybrid_mine():
502
+ # Mine GTX bills first if denominations provided
503
+ if bill_denominations:
504
+ for denomination in bill_denominations:
505
+ if not self.mining_active:
506
+ break
507
+
508
+ print(f"Mining GTX ${denomination:,} bill...")
509
+ bill_result = self.mine_bill(denomination, miner_address)
510
+
511
+ if callback:
512
+ callback({"type": "bill", "data": bill_result})
513
+
514
+ time.sleep(1)
515
+
516
+ # Switch to continuous block mining
517
+ block_height = self.blockchain_manager.get_blockchain_height() + 1
518
+ latest_block = self.blockchain_manager.get_latest_block()
519
+ previous_hash = latest_block.get('hash', '0' * 64) if latest_block else '0' * 64
520
+
521
+ while self.mining_active:
522
+ pending_count = len(self.mempool_manager.get_pending_transactions())
523
+
524
+ if pending_count > 0:
525
+ print(f"🔄 Mining transaction block #{block_height}...")
526
+
527
+ block_result = self.mine_transaction_block(miner_address, previous_hash, block_height)
528
+
529
+ if block_result.get("success"):
530
+ if callback:
531
+ callback({"type": "block", "data": block_result})
532
+
533
+ block_height += 1
534
+ previous_hash = block_result["block"]["hash"]
535
+
536
+ time.sleep(2)
537
+ else:
538
+ print("⏳ No transactions, checking again in 10s...")
539
+ time.sleep(10)
540
+
541
+ self.current_thread = threading.Thread(target=hybrid_mine, daemon=True)
542
+ self.current_thread.start()
543
+ print("Started hybrid mining (bills + blocks)")
544
+ return True
545
+
546
+ def get_mining_stats(self) -> Dict:
547
+ """Get comprehensive mining statistics"""
548
+ pending_txs = self.mempool_manager.get_pending_transactions()
549
+
550
+ return {
551
+ "mining_active": self.mining_active,
552
+ "bills_mined": self.mining_stats["bills_mined"],
553
+ "blocks_mined": self.mining_stats["blocks_mined"],
554
+ "total_mining_time": self.mining_stats["total_mining_time"],
555
+ "total_hash_attempts": self.mining_stats["total_hash_attempts"],
556
+ "mempool_size": len(pending_txs),
557
+ "pending_transactions": [
558
+ {
559
+ "hash": tx.get('hash', '')[:16] + '...',
560
+ "from": tx.get('from', ''),
561
+ "to": tx.get('to', ''),
562
+ "amount": tx.get('amount', 0),
563
+ "fee": tx.get('fee', 0),
564
+ "type": tx.get('type', 'unknown')
565
+ }
566
+ for tx in pending_txs[:5] # Show first 5
567
+ ],
568
+ "average_hashrate": (
569
+ self.mining_stats["total_hash_attempts"] / self.mining_stats["total_mining_time"]
570
+ if self.mining_stats["total_mining_time"] > 0 else 0
571
+ )
572
+ }
573
+
102
574
  def stop_mining(self):
103
575
  """Stop all mining activities"""
104
576
  self.mining_active = False
105
- if self.current_thread:
577
+ if self.current_thread and self.current_thread.is_alive():
106
578
  self.current_thread.join(timeout=5)
107
- print("🛑 Mining stopped")
579
+ print("Mining stopped")
580
+
581
+ stats = self.get_mining_stats()
582
+ print(f"Final statistics:")
583
+ print(f"Bills mined: {stats['bills_mined']}")
584
+ print(f"Blocks mined: {stats['blocks_mined']}")
585
+ print(f"Total mining time: {stats['total_mining_time']:.2f}s")
586
+ print(f"Average hashrate: {stats['average_hashrate']:,.0f} H/s")
587
+ print(f"Mempool size: {stats['mempool_size']} transactions")
588
+
589
+ def submit_transaction(self, transaction: Dict) -> bool:
590
+ """Submit transaction to mempool for mining"""
591
+ try:
592
+ success = self.mempool_manager.add_transaction(transaction)
593
+ if success:
594
+ print(f"📨 Added transaction to mining mempool: {transaction.get('hash', '')[:16]}...")
595
+ return success
596
+ except Exception as e:
597
+ print(f"Error submitting transaction: {e}")
598
+ return False
599
+
600
+ def get_network_status(self) -> Dict:
601
+ """Get current network and blockchain status"""
602
+ try:
603
+ height = self.blockchain_manager.get_blockchain_height()
604
+ connected = self.blockchain_manager.check_network_connection()
605
+ mempool_size = len(self.mempool_manager.get_pending_transactions())
606
+
607
+ return {
608
+ "network_connected": connected,
609
+ "blockchain_height": height,
610
+ "mempool_size": mempool_size,
611
+ "mining_active": self.mining_active
612
+ }
613
+ except Exception as e:
614
+ return {
615
+ "network_connected": False,
616
+ "error": str(e)
617
+ }