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.

Files changed (48) hide show
  1. lunalib/core/__init__.py +14 -0
  2. lunalib/core/blockchain.py +183 -3
  3. lunalib/core/daemon.py +494 -0
  4. lunalib/core/p2p.py +361 -0
  5. lunalib/core/sm2.py +723 -723
  6. lunalib/core/wallet.py +714 -478
  7. lunalib/core/wallet_manager.py +638 -638
  8. lunalib/core/wallet_sync_helper.py +163 -163
  9. lunalib/gtx/digital_bill.py +10 -2
  10. lunalib/gtx/genesis.py +23 -24
  11. lunalib/mining/__init__.py +5 -0
  12. lunalib/mining/cuda_manager.py +23 -28
  13. lunalib/mining/difficulty.py +38 -0
  14. lunalib/mining/miner.py +526 -17
  15. lunalib/storage/cache.py +13 -4
  16. lunalib/storage/database.py +14 -5
  17. lunalib/storage/encryption.py +11 -2
  18. lunalib/transactions/security.py +19 -10
  19. lunalib/transactions/transactions.py +50 -41
  20. lunalib-1.7.2.dist-info/METADATA +27 -0
  21. lunalib-1.7.2.dist-info/RECORD +33 -0
  22. lunalib-1.7.2.dist-info/top_level.txt +1 -0
  23. core/__init__.py +0 -0
  24. core/blockchain.py +0 -172
  25. core/crypto.py +0 -32
  26. core/wallet.py +0 -408
  27. gtx/__init__.py +0 -0
  28. gtx/bill_registry.py +0 -122
  29. gtx/digital_bill.py +0 -273
  30. gtx/genesis.py +0 -338
  31. lunalib/requirements.txt +0 -44
  32. lunalib-1.5.1.dist-info/METADATA +0 -283
  33. lunalib-1.5.1.dist-info/RECORD +0 -53
  34. lunalib-1.5.1.dist-info/entry_points.txt +0 -2
  35. lunalib-1.5.1.dist-info/top_level.txt +0 -6
  36. mining/__init__.py +0 -0
  37. mining/cuda_manager.py +0 -137
  38. mining/difficulty.py +0 -106
  39. mining/miner.py +0 -107
  40. storage/__init__.py +0 -0
  41. storage/cache.py +0 -148
  42. storage/database.py +0 -222
  43. storage/encryption.py +0 -105
  44. transactions/__init__.py +0 -0
  45. transactions/security.py +0 -172
  46. transactions/transactions.py +0 -424
  47. transactions/validator.py +0 -71
  48. {lunalib-1.5.1.dist-info โ†’ lunalib-1.7.2.dist-info}/WHEEL +0 -0
lunalib/mining/miner.py CHANGED
@@ -1,5 +1,14 @@
1
1
  # lunalib/mining/miner.py
2
2
  import time
3
+ import sys
4
+
5
+ # --- Unicode-safe print for Windows console ---
6
+ def safe_print(*args, **kwargs):
7
+ try:
8
+ print(*args, **kwargs)
9
+ except UnicodeEncodeError:
10
+ encoding = getattr(sys.stdout, 'encoding', 'utf-8')
11
+ print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
3
12
  import hashlib
4
13
  import json
5
14
  import threading
@@ -9,6 +18,7 @@ from ..gtx.digital_bill import DigitalBill
9
18
  from ..transactions.transactions import TransactionManager
10
19
  from ..core.blockchain import BlockchainManager
11
20
  from ..core.mempool import MempoolManager
21
+ from ..mining.cuda_manager import CUDAManager
12
22
 
13
23
  class GenesisMiner:
14
24
  """Mines GTX Genesis bills AND regular transfer transactions with configurable difficulty"""
@@ -28,7 +38,7 @@ class GenesisMiner:
28
38
  "total_hash_attempts": 0
29
39
  }
30
40
 
31
- print("๐Ÿ”ง GenesisMiner initialized with integrated lunalib components")
41
+ safe_print("๐Ÿ”ง GenesisMiner initialized with integrated lunalib components")
32
42
 
33
43
  def mine_bill(self, denomination: float, user_address: str, bill_data: Dict = None) -> Dict:
34
44
  """Mine a GTX Genesis bill using DigitalBill system"""
@@ -43,7 +53,7 @@ class GenesisMiner:
43
53
  bill_data=bill_data or {}
44
54
  )
45
55
 
46
- print(f"โ›๏ธ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
56
+ safe_print(f"โ›๏ธ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
47
57
 
48
58
  start_time = time.time()
49
59
  mining_result = self._perform_bill_mining(digital_bill, difficulty)
@@ -63,10 +73,10 @@ class GenesisMiner:
63
73
  self.mining_stats["total_mining_time"] += mining_time
64
74
  self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
65
75
 
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]}...")
76
+ safe_print(f"โœ… Successfully mined GTX ${denomination:,} bill!")
77
+ safe_print(f"โฑ๏ธ Mining time: {mining_time:.2f}s")
78
+ safe_print(f" Hash attempts: {mining_result['nonce']:,}")
79
+ safe_print(f"๐Ÿ”— Bill hash: {mining_result['hash'][:32]}...")
70
80
 
71
81
  # Convert to GTX Genesis transaction
72
82
  gtx_transaction = self._create_gtx_genesis_transaction(bill)
@@ -105,8 +115,8 @@ class GenesisMiner:
105
115
  # Calculate block difficulty
106
116
  difficulty = self.difficulty_system.get_transaction_block_difficulty(transactions)
107
117
 
108
- print(f"โ›๏ธ Mining Transaction Block #{block_height} - Difficulty: {difficulty} zeros")
109
- print(f"๐Ÿ“ฆ Transactions: {len(transactions)} | Previous Hash: {previous_hash[:16]}...")
118
+ safe_print(f"โ›๏ธ Mining Transaction Block #{block_height} - Difficulty: {difficulty} zeros")
119
+ safe_print(f"๐Ÿ“ฆ Transactions: {len(transactions)} | Previous Hash: {previous_hash[:16]}...")
110
120
 
111
121
  # Create block structure for mining
112
122
  block_data = {
@@ -162,20 +172,20 @@ class GenesisMiner:
162
172
  self.mining_stats["total_mining_time"] += mining_time
163
173
  self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
164
174
 
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]}...")
175
+ safe_print(f"โœ… Successfully mined and validated Transaction Block #{block_height}!")
176
+ safe_print(f"โฑ๏ธ Mining time: {mining_time:.2f}s")
177
+ safe_print(f"๐Ÿ’ฐ Block reward: {block['reward']:.6f} LUN")
178
+ safe_print(f" Transactions: {block['transaction_count']}")
179
+ safe_print(f"๐Ÿ”— Block hash: {mining_result['hash'][:32]}...")
170
180
 
171
181
  # Submit block to blockchain
172
182
  submission_success = self.blockchain_manager.submit_mined_block(block)
173
183
  if submission_success:
174
- print("โœ… Block successfully submitted to blockchain!")
184
+ safe_print("โœ… Block successfully submitted to blockchain!")
175
185
  # Clear mined transactions from local mempool
176
186
  self._clear_mined_transactions(transactions)
177
187
  else:
178
- print("โš ๏ธ Block mined but submission failed")
188
+ safe_print("โš ๏ธ Block mined but submission failed")
179
189
 
180
190
  return {
181
191
  "success": True,
@@ -289,7 +299,7 @@ class GenesisMiner:
289
299
  # Calculate merkleroot from transactions
290
300
  merkleroot = self._calculate_merkleroot(transactions)
291
301
 
292
- print(f"๐Ÿ“Š Mining proof components:")
302
+ print(f" Mining proof components:")
293
303
  print(f" Block hash: {block_hash[:16]}...")
294
304
  print(f" Difficulty: {difficulty}")
295
305
  print(f" Nonce: {nonce}")
@@ -614,4 +624,503 @@ class GenesisMiner:
614
624
  return {
615
625
  "network_connected": False,
616
626
  "error": str(e)
617
- }
627
+ }
628
+
629
+ class Miner:
630
+ """
631
+ Robust miner class that integrates with BlockchainManager, MempoolManager,
632
+ DifficultySystem, and security validation to mine all transaction types.
633
+ """
634
+
635
+ def __init__(self, config, data_manager, mining_started_callback=None, mining_completed_callback=None, block_mined_callback=None):
636
+ self.config = config
637
+ self.data_manager = data_manager
638
+ self.is_mining = False
639
+ self.blocks_mined = 0
640
+ self.total_reward = 0.0
641
+ self.mining_started_callback = mining_started_callback
642
+ self.mining_completed_callback = mining_completed_callback
643
+ self.block_mined_callback = block_mined_callback
644
+
645
+ self.mining_history = self.data_manager.load_mining_history()
646
+
647
+ # Initialize lunalib components
648
+ self.blockchain_manager = BlockchainManager(endpoint_url=config.node_url)
649
+ self.mempool_manager = MempoolManager([config.node_url])
650
+ self.difficulty_system = DifficultySystem()
651
+ self.cuda_manager = CUDAManager()
652
+
653
+ # Import security components
654
+ try:
655
+ from ..transactions.security import SecurityManager
656
+ from ..transactions.validator import TransactionValidator
657
+ self.security_manager = SecurityManager()
658
+ self.transaction_validator = TransactionValidator()
659
+ except ImportError:
660
+ self.security_manager = None
661
+ self.transaction_validator = None
662
+
663
+ self.current_hash = ""
664
+ self.current_nonce = 0
665
+ self.hash_rate = 0
666
+ self.mining_thread = None
667
+ self.should_stop_mining = False
668
+
669
+ def mine_block(self) -> tuple[bool, str, Optional[Dict]]:
670
+ """
671
+ Mine a block from the mempool with proper validation and difficulty calculation.
672
+ Returns: (success, message, block_data)
673
+ """
674
+ try:
675
+ # Get the latest block from the blockchain
676
+ latest_block = self.blockchain_manager.get_latest_block()
677
+ if not latest_block:
678
+ return False, "Could not get latest block from server", None
679
+
680
+ current_index = latest_block.get('index', 0)
681
+ previous_hash = latest_block.get('hash', '0' * 64)
682
+ new_index = current_index + 1
683
+
684
+ # Get fresh transactions from mempool
685
+ mempool = self._get_fresh_mempool()
686
+
687
+ # Validate all transactions
688
+ valid_transactions = self._validate_transactions(mempool)
689
+
690
+ # Calculate block difficulty based on transactions
691
+ block_difficulty = self._calculate_block_difficulty(valid_transactions)
692
+
693
+ # Calculate block reward:
694
+ # - Empty blocks use LINEAR system: difficulty 1 = 1 LKC, difficulty 2 = 2 LKC, etc.
695
+ # - Blocks with transactions use EXPONENTIAL system: 10^(difficulty-1)
696
+ if not valid_transactions:
697
+ # Empty block: linear reward
698
+ total_reward = float(block_difficulty)
699
+ reward_tx = self._create_empty_block_reward(new_index, block_difficulty, total_reward)
700
+ valid_transactions = [reward_tx]
701
+ else:
702
+ # Regular block: exponential reward
703
+ total_reward = self._calculate_exponential_block_reward(block_difficulty)
704
+
705
+ # Create block data
706
+ block_data = {
707
+ 'index': new_index,
708
+ 'previous_hash': previous_hash,
709
+ 'timestamp': time.time(),
710
+ 'transactions': valid_transactions,
711
+ 'miner': self.config.miner_address,
712
+ 'difficulty': block_difficulty,
713
+ 'nonce': 0,
714
+ 'reward': total_reward,
715
+ 'hash': ''
716
+ }
717
+
718
+ # Try CUDA mining first if available
719
+ if self.cuda_manager and self.cuda_manager.cuda_available:
720
+ cuda_result = self._cuda_mine(block_data, block_difficulty)
721
+ if cuda_result:
722
+ return self._finalize_block(cuda_result, 'cuda', total_reward)
723
+
724
+ # Fallback to CPU mining
725
+ cpu_result = self._cpu_mine(block_data, block_difficulty)
726
+ if cpu_result:
727
+ return self._finalize_block(cpu_result, 'cpu', total_reward)
728
+
729
+ return False, "Mining timeout - no solution found", None
730
+
731
+ except Exception as e:
732
+ return False, f"Mining error: {str(e)}", None
733
+
734
+ def _get_fresh_mempool(self) -> List[Dict]:
735
+ """Get fresh mempool transactions with validation"""
736
+ try:
737
+ mempool = self.mempool_manager.get_pending_transactions()
738
+ if not mempool:
739
+ mempool = self.blockchain_manager.get_mempool()
740
+ return mempool if mempool else []
741
+ except Exception as e:
742
+ safe_print(f"Error fetching mempool: {e}")
743
+ return []
744
+
745
+ def _validate_transactions(self, transactions: List[Dict]) -> List[Dict]:
746
+ """Validate transactions using security manager"""
747
+ valid_transactions = []
748
+
749
+ for tx in transactions:
750
+ try:
751
+ # Basic validation
752
+ if not self._validate_transaction_structure(tx):
753
+ continue
754
+
755
+ # Security validation if available
756
+ if self.transaction_validator:
757
+ if not self.transaction_validator.validate_transaction(tx):
758
+ continue
759
+
760
+ valid_transactions.append(tx)
761
+ except Exception as e:
762
+ safe_print(f"Transaction validation error: {e}")
763
+ continue
764
+
765
+ return valid_transactions
766
+
767
+ def _validate_transaction_structure(self, tx: Dict) -> bool:
768
+ """Basic transaction structure validation"""
769
+ required_fields = ['type', 'timestamp']
770
+
771
+ for field in required_fields:
772
+ if field not in tx:
773
+ return False
774
+
775
+ tx_type = tx.get('type')
776
+
777
+ if tx_type == 'transaction':
778
+ if not all(k in tx for k in ['from', 'to', 'amount']):
779
+ return False
780
+ elif tx_type == 'genesis_bill':
781
+ if 'denomination' not in tx:
782
+ return False
783
+ elif tx_type == 'reward':
784
+ if not all(k in tx for k in ['to', 'amount']):
785
+ return False
786
+
787
+ return True
788
+
789
+ def _calculate_block_difficulty(self, transactions: List[Dict]) -> int:
790
+ """Calculate block difficulty based on transactions using DifficultySystem"""
791
+ if not transactions:
792
+ return self.config.difficulty
793
+
794
+ max_difficulty = self.config.difficulty
795
+
796
+ for tx in transactions:
797
+ tx_type = tx.get('type')
798
+
799
+ if tx_type == 'genesis_bill':
800
+ denomination = tx.get('denomination', 0)
801
+ tx_difficulty = self.difficulty_system.get_bill_difficulty(denomination)
802
+ max_difficulty = max(max_difficulty, tx_difficulty)
803
+ elif tx_type == 'transaction':
804
+ amount = tx.get('amount', 0)
805
+ tx_difficulty = self.difficulty_system.get_transaction_difficulty(amount)
806
+ max_difficulty = max(max_difficulty, tx_difficulty)
807
+ elif tx_type == 'reward':
808
+ max_difficulty = max(max_difficulty, 1)
809
+
810
+ return max(max_difficulty, self.config.difficulty)
811
+
812
+ def _calculate_block_reward(self, transactions: List[Dict]) -> float:
813
+ """Calculate total block reward based on transactions using DifficultySystem"""
814
+ total_reward = 0.0
815
+
816
+ for tx in transactions:
817
+ tx_type = tx.get('type')
818
+
819
+ if tx_type == 'genesis_bill':
820
+ denomination = tx.get('denomination', 0)
821
+ # Use difficulty system to calculate proper reward
822
+ bill_difficulty = self.difficulty_system.get_bill_difficulty(denomination)
823
+ avg_mining_time = 15.0 # Will be updated with actual time
824
+ bill_reward = self.difficulty_system.calculate_mining_reward(denomination, avg_mining_time)
825
+ total_reward += bill_reward
826
+ elif tx_type == 'transaction':
827
+ fee = tx.get('fee', 0)
828
+ total_reward += fee
829
+ elif tx_type == 'reward':
830
+ reward_amount = tx.get('amount', 0)
831
+ total_reward += reward_amount
832
+
833
+ # Minimum reward for empty blocks
834
+ if total_reward == 0:
835
+ total_reward = 1.0
836
+
837
+ return total_reward
838
+
839
+ def _calculate_exponential_block_reward(self, difficulty: int) -> float:
840
+ """Calculate block reward using exponential difficulty system
841
+
842
+ Uses the new exponential reward system:
843
+ difficulty 1 = 1 LKC
844
+ difficulty 2 = 10 LKC
845
+ difficulty 3 = 100 LKC
846
+ difficulty 9 = 100,000,000 LKC
847
+ """
848
+ return self.difficulty_system.calculate_block_reward(difficulty)
849
+
850
+ def _create_empty_block_reward(self, block_index: int, difficulty: int, reward_amount: float) -> Dict:
851
+ """Create reward transaction for empty blocks using LINEAR reward system (difficulty = reward)"""
852
+ return {
853
+ 'type': 'reward',
854
+ 'from': 'network',
855
+ 'to': self.config.miner_address,
856
+ 'amount': reward_amount,
857
+ 'timestamp': time.time(),
858
+ 'block_height': block_index,
859
+ 'difficulty': difficulty,
860
+ 'hash': f"reward_{block_index}_{int(time.time())}",
861
+ 'description': f'Empty block mining reward (Linear: Difficulty {difficulty} = {reward_amount} LKC)',
862
+ 'is_empty_block': True
863
+ }
864
+
865
+ def _cuda_mine(self, block_data: Dict, difficulty: int) -> Optional[Dict]:
866
+ """Mine using CUDA acceleration"""
867
+ try:
868
+ safe_print("Attempting CUDA mining...")
869
+ cuda_result = self.cuda_manager.cuda_mine_batch(
870
+ block_data, difficulty, batch_size=100000
871
+ )
872
+ if cuda_result and cuda_result.get('success'):
873
+ block_data['hash'] = cuda_result['hash']
874
+ block_data['nonce'] = cuda_result['nonce']
875
+ return block_data
876
+ except Exception as e:
877
+ safe_print(f"CUDA mining failed: {e}")
878
+ return None
879
+
880
+ def _cpu_mine(self, block_data: Dict, difficulty: int) -> Optional[Dict]:
881
+ """Mine using CPU"""
882
+ safe_print("Using CPU mining...")
883
+ start_time = time.time()
884
+ target = "0" * difficulty
885
+ nonce = 0
886
+ hash_count = 0
887
+ last_hash_update = start_time
888
+
889
+ while not self.should_stop_mining and nonce < 1000000:
890
+ # Calculate block hash
891
+ block_hash = self._calculate_block_hash(
892
+ block_data['index'],
893
+ block_data['previous_hash'],
894
+ block_data['timestamp'],
895
+ block_data['transactions'],
896
+ nonce,
897
+ block_data['miner'],
898
+ difficulty
899
+ )
900
+
901
+ if block_hash.startswith(target):
902
+ block_data['hash'] = block_hash
903
+ block_data['nonce'] = nonce
904
+ return block_data
905
+
906
+ nonce += 1
907
+ hash_count += 1
908
+ self.current_nonce = nonce
909
+ self.current_hash = block_hash
910
+
911
+ # Update hash rate
912
+ current_time = time.time()
913
+ if current_time - last_hash_update >= 1:
914
+ self.hash_rate = hash_count / (current_time - last_hash_update)
915
+ hash_count = 0
916
+ last_hash_update = current_time
917
+
918
+ if nonce % 1000 == 0 and not self.is_mining:
919
+ return None
920
+
921
+ return None
922
+
923
+ def _calculate_block_hash(self, index: int, previous_hash: str, timestamp: float,
924
+ transactions: List[Dict], nonce: int, miner: str, difficulty: int) -> str:
925
+ """Calculate SHA-256 hash of a block matching server validation"""
926
+ try:
927
+ block_data = {
928
+ "difficulty": int(difficulty),
929
+ "index": int(index),
930
+ "miner": str(miner),
931
+ "nonce": int(nonce),
932
+ "previous_hash": str(previous_hash),
933
+ "timestamp": float(timestamp),
934
+ "transactions": [], # Empty for mining proof
935
+ "version": "1.0"
936
+ }
937
+
938
+ block_string = json.dumps(block_data, sort_keys=True)
939
+ calculated_hash = hashlib.sha256(block_string.encode()).hexdigest()
940
+ return calculated_hash
941
+
942
+ except Exception as e:
943
+ safe_print(f"Hash calculation error: {e}")
944
+ return "0" * 64
945
+
946
+ def _finalize_block(self, block_data: Dict, method: str, total_reward: float) -> tuple[bool, str, Dict]:
947
+ """Finalize mined block with proper record keeping and blockchain submission"""
948
+ mining_time = time.time() - block_data.get('timestamp', time.time())
949
+
950
+ # Validate block before submission
951
+ validation_result = self._validate_mined_block(block_data)
952
+ if not validation_result[0]:
953
+ safe_print(f"โŒ Block validation failed: {validation_result[1]}")
954
+ return False, f"Block validation failed: {validation_result[1]}", None
955
+
956
+ # Block reward is already calculated based on difficulty (exponential system)
957
+ final_reward = block_data['reward']
958
+
959
+ # Submit block to blockchain
960
+ try:
961
+ submission_success = self.blockchain_manager.submit_mined_block(block_data)
962
+
963
+ if not submission_success:
964
+ safe_print(f"โš ๏ธ Block #{block_data['index']} submission failed")
965
+ return False, f"Block #{block_data['index']} mined but submission failed", None
966
+
967
+ safe_print(f"โœ… Block #{block_data['index']} submitted successfully (Reward: {final_reward} LKC)")
968
+
969
+ # Clear mined transactions from mempool
970
+ self._clear_transactions_from_mempool(block_data['transactions'])
971
+
972
+ except Exception as e:
973
+ safe_print(f"โŒ Block submission error: {e}")
974
+ return False, f"Block submission error: {str(e)}", None
975
+
976
+ # Record mining history
977
+ mining_record = {
978
+ 'block_index': block_data['index'],
979
+ 'timestamp': time.time(),
980
+ 'mining_time': mining_time,
981
+ 'difficulty': block_data['difficulty'],
982
+ 'nonce': block_data['nonce'],
983
+ 'hash': block_data['hash'],
984
+ 'method': method,
985
+ 'reward': final_reward,
986
+ 'status': 'success'
987
+ }
988
+ self.mining_history.append(mining_record)
989
+ self.save_mining_history()
990
+
991
+ self.blocks_mined += 1
992
+ self.total_reward += final_reward
993
+
994
+ if self.mining_completed_callback:
995
+ self.mining_completed_callback(True, f"Block #{block_data['index']} mined - Reward: {final_reward}")
996
+
997
+ if self.block_mined_callback:
998
+ self.block_mined_callback(block_data)
999
+
1000
+ return True, f"Block #{block_data['index']} mined - Reward: {final_reward}", block_data
1001
+
1002
+ def _validate_mined_block(self, block: Dict) -> tuple:
1003
+ """Validate mined block before submission
1004
+
1005
+ Returns: (is_valid, error_message)
1006
+ """
1007
+ # Validate structure
1008
+ is_valid, error = self.difficulty_system.validate_block_structure(block)
1009
+ if not is_valid:
1010
+ return False, error
1011
+
1012
+ # Validate hash meets difficulty
1013
+ block_hash = block.get('hash', '')
1014
+ difficulty = block.get('difficulty', 0)
1015
+
1016
+ if not self.difficulty_system.validate_block_hash(block_hash, difficulty):
1017
+ return False, f"Hash does not meet difficulty {difficulty} requirement"
1018
+
1019
+ # Validate previous hash (get from blockchain)
1020
+ try:
1021
+ latest_block = self.blockchain_manager.get_latest_block()
1022
+ if latest_block:
1023
+ expected_prev_hash = latest_block.get('hash', '')
1024
+ if block.get('previous_hash') != expected_prev_hash:
1025
+ return False, f"Previous hash mismatch: expected {expected_prev_hash[:16]}..., got {block.get('previous_hash', '')[:16]}..."
1026
+ except Exception as e:
1027
+ safe_print(f"โš ๏ธ Could not validate previous hash: {e}")
1028
+
1029
+ # Validate reward matches difficulty
1030
+ # Empty blocks use LINEAR system (difficulty = reward)
1031
+ # Regular blocks use EXPONENTIAL system (10^(difficulty-1))
1032
+ transactions = block.get('transactions', [])
1033
+ is_empty_block = len(transactions) == 1 and transactions[0].get('is_empty_block', False)
1034
+
1035
+ if is_empty_block:
1036
+ # Empty block: linear reward
1037
+ expected_reward = float(difficulty)
1038
+ else:
1039
+ # Regular block: exponential reward
1040
+ expected_reward = self.difficulty_system.calculate_block_reward(difficulty)
1041
+
1042
+ actual_reward = block.get('reward', 0)
1043
+
1044
+ # Allow some tolerance for floating point comparison
1045
+ if abs(actual_reward - expected_reward) > 0.01:
1046
+ reward_type = "Linear" if is_empty_block else "Exponential"
1047
+ return False, f"Reward mismatch ({reward_type}): expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC"
1048
+
1049
+ reward_type = "Linear" if is_empty_block else "Exponential"
1050
+ safe_print(f"โœ… Block validation passed ({reward_type}): Hash meets difficulty {difficulty}, Reward: {actual_reward} LKC")
1051
+ return True, ""
1052
+
1053
+ def _clear_transactions_from_mempool(self, transactions: List[Dict]):
1054
+ """Remove mined transactions from mempool"""
1055
+ try:
1056
+ for tx in transactions:
1057
+ # Skip reward transactions (they were created during mining)
1058
+ if tx.get('type') == 'reward' and tx.get('from') == 'network':
1059
+ continue
1060
+
1061
+ tx_hash = tx.get('hash')
1062
+ if tx_hash:
1063
+ # Remove from mempool manager if available
1064
+ try:
1065
+ self.mempool_manager.remove_transaction(tx_hash)
1066
+ except:
1067
+ pass # Silent fail if method doesn't exist
1068
+
1069
+ safe_print(f"๐Ÿงน Cleared {len(transactions)} transactions from mempool")
1070
+
1071
+ except Exception as e:
1072
+ safe_print(f"โš ๏ธ Error clearing mempool: {e}")
1073
+
1074
+ def _calculate_final_reward(self, transactions: List[Dict], actual_mining_time: float) -> float:
1075
+ """Calculate final reward using actual mining time"""
1076
+ total_reward = 0.0
1077
+
1078
+ for tx in transactions:
1079
+ tx_type = tx.get('type')
1080
+
1081
+ if tx_type == 'genesis_bill':
1082
+ denomination = tx.get('denomination', 0)
1083
+ bill_reward = self.difficulty_system.calculate_mining_reward(denomination, actual_mining_time)
1084
+ total_reward += bill_reward
1085
+ elif tx_type == 'transaction':
1086
+ total_reward += tx.get('fee', 0)
1087
+ elif tx_type == 'reward':
1088
+ total_reward += tx.get('amount', 0)
1089
+
1090
+ if total_reward == 0:
1091
+ total_reward = 1.0
1092
+
1093
+ return total_reward
1094
+
1095
+ def save_mining_history(self):
1096
+ """Save mining history to storage"""
1097
+ self.data_manager.save_mining_history(self.mining_history)
1098
+
1099
+ def start_mining(self):
1100
+ """Start the mining process"""
1101
+ if self.is_mining:
1102
+ return
1103
+
1104
+ self.is_mining = True
1105
+ self.should_stop_mining = False
1106
+
1107
+ if self.mining_started_callback:
1108
+ self.mining_started_callback()
1109
+
1110
+ def stop_mining(self):
1111
+ """Stop the mining process"""
1112
+ self.is_mining = False
1113
+ self.should_stop_mining = True
1114
+ if self.mining_thread and self.mining_thread.is_alive():
1115
+ self.mining_thread.join()
1116
+
1117
+ def get_mining_stats(self):
1118
+ """Return the current mining statistics"""
1119
+ return {
1120
+ "blocks_mined": self.blocks_mined,
1121
+ "total_reward": self.total_reward,
1122
+ "current_hash": self.current_hash,
1123
+ "current_nonce": self.current_nonce,
1124
+ "hash_rate": self.hash_rate,
1125
+ "mining_history": len(self.mining_history)
1126
+ }
lunalib/storage/cache.py CHANGED
@@ -1,4 +1,13 @@
1
1
  import os
2
+ import sys
3
+
4
+ # --- Unicode-safe print for Windows console ---
5
+ def safe_print(*args, **kwargs):
6
+ try:
7
+ print(*args, **kwargs)
8
+ except UnicodeEncodeError:
9
+ encoding = getattr(sys.stdout, 'encoding', 'utf-8')
10
+ print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
2
11
  import sqlite3
3
12
  import pickle
4
13
  import gzip
@@ -66,7 +75,7 @@ class BlockchainCache:
66
75
  conn.commit()
67
76
  conn.close()
68
77
  except Exception as e:
69
- print(f"Cache save error: {e}")
78
+ safe_print(f"Cache save error: {e}")
70
79
 
71
80
  def get_block(self, height: int) -> Optional[Dict]:
72
81
  """Get block from cache"""
@@ -88,7 +97,7 @@ class BlockchainCache:
88
97
 
89
98
  conn.close()
90
99
  except Exception as e:
91
- print(f"Cache read error: {e}")
100
+ safe_print(f"Cache read error: {e}")
92
101
 
93
102
  return None
94
103
 
@@ -116,7 +125,7 @@ class BlockchainCache:
116
125
  continue
117
126
 
118
127
  except Exception as e:
119
- print(f"Block range cache error: {e}")
128
+ safe_print(f"Block range cache error: {e}")
120
129
 
121
130
  return blocks
122
131
 
@@ -145,4 +154,4 @@ class BlockchainCache:
145
154
  conn.commit()
146
155
  conn.close()
147
156
  except Exception as e:
148
- print(f"Cache cleanup error: {e}")
157
+ safe_print(f"Cache cleanup error: {e}")