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/mining/miner.py
CHANGED
|
@@ -18,6 +18,7 @@ from ..gtx.digital_bill import DigitalBill
|
|
|
18
18
|
from ..transactions.transactions import TransactionManager
|
|
19
19
|
from ..core.blockchain import BlockchainManager
|
|
20
20
|
from ..core.mempool import MempoolManager
|
|
21
|
+
from ..mining.cuda_manager import CUDAManager
|
|
21
22
|
|
|
22
23
|
class GenesisMiner:
|
|
23
24
|
"""Mines GTX Genesis bills AND regular transfer transactions with configurable difficulty"""
|
|
@@ -74,7 +75,7 @@ class GenesisMiner:
|
|
|
74
75
|
|
|
75
76
|
safe_print(f"✅ Successfully mined GTX ${denomination:,} bill!")
|
|
76
77
|
safe_print(f"⏱️ Mining time: {mining_time:.2f}s")
|
|
77
|
-
safe_print(f"
|
|
78
|
+
safe_print(f" Hash attempts: {mining_result['nonce']:,}")
|
|
78
79
|
safe_print(f"🔗 Bill hash: {mining_result['hash'][:32]}...")
|
|
79
80
|
|
|
80
81
|
# Convert to GTX Genesis transaction
|
|
@@ -174,7 +175,7 @@ class GenesisMiner:
|
|
|
174
175
|
safe_print(f"✅ Successfully mined and validated Transaction Block #{block_height}!")
|
|
175
176
|
safe_print(f"⏱️ Mining time: {mining_time:.2f}s")
|
|
176
177
|
safe_print(f"💰 Block reward: {block['reward']:.6f} LUN")
|
|
177
|
-
safe_print(f"
|
|
178
|
+
safe_print(f" Transactions: {block['transaction_count']}")
|
|
178
179
|
safe_print(f"🔗 Block hash: {mining_result['hash'][:32]}...")
|
|
179
180
|
|
|
180
181
|
# Submit block to blockchain
|
|
@@ -298,7 +299,7 @@ class GenesisMiner:
|
|
|
298
299
|
# Calculate merkleroot from transactions
|
|
299
300
|
merkleroot = self._calculate_merkleroot(transactions)
|
|
300
301
|
|
|
301
|
-
print(f"
|
|
302
|
+
print(f" Mining proof components:")
|
|
302
303
|
print(f" Block hash: {block_hash[:16]}...")
|
|
303
304
|
print(f" Difficulty: {difficulty}")
|
|
304
305
|
print(f" Nonce: {nonce}")
|
|
@@ -623,4 +624,484 @@ class GenesisMiner:
|
|
|
623
624
|
return {
|
|
624
625
|
"network_connected": False,
|
|
625
626
|
"error": str(e)
|
|
626
|
-
}
|
|
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
|
+
# If no valid transactions, create a reward transaction
|
|
691
|
+
if not valid_transactions:
|
|
692
|
+
reward_tx = self._create_empty_block_reward(new_index)
|
|
693
|
+
valid_transactions = [reward_tx]
|
|
694
|
+
|
|
695
|
+
# Calculate block difficulty based on transactions
|
|
696
|
+
block_difficulty = self._calculate_block_difficulty(valid_transactions)
|
|
697
|
+
|
|
698
|
+
# Calculate block reward using exponential difficulty system
|
|
699
|
+
total_reward = self._calculate_exponential_block_reward(block_difficulty)
|
|
700
|
+
|
|
701
|
+
# Create block data
|
|
702
|
+
block_data = {
|
|
703
|
+
'index': new_index,
|
|
704
|
+
'previous_hash': previous_hash,
|
|
705
|
+
'timestamp': time.time(),
|
|
706
|
+
'transactions': valid_transactions,
|
|
707
|
+
'miner': self.config.miner_address,
|
|
708
|
+
'difficulty': block_difficulty,
|
|
709
|
+
'nonce': 0,
|
|
710
|
+
'reward': total_reward,
|
|
711
|
+
'hash': ''
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
# Try CUDA mining first if available
|
|
715
|
+
if self.cuda_manager and self.cuda_manager.cuda_available:
|
|
716
|
+
cuda_result = self._cuda_mine(block_data, block_difficulty)
|
|
717
|
+
if cuda_result:
|
|
718
|
+
return self._finalize_block(cuda_result, 'cuda', total_reward)
|
|
719
|
+
|
|
720
|
+
# Fallback to CPU mining
|
|
721
|
+
cpu_result = self._cpu_mine(block_data, block_difficulty)
|
|
722
|
+
if cpu_result:
|
|
723
|
+
return self._finalize_block(cpu_result, 'cpu', total_reward)
|
|
724
|
+
|
|
725
|
+
return False, "Mining timeout - no solution found", None
|
|
726
|
+
|
|
727
|
+
except Exception as e:
|
|
728
|
+
return False, f"Mining error: {str(e)}", None
|
|
729
|
+
|
|
730
|
+
def _get_fresh_mempool(self) -> List[Dict]:
|
|
731
|
+
"""Get fresh mempool transactions with validation"""
|
|
732
|
+
try:
|
|
733
|
+
mempool = self.mempool_manager.get_pending_transactions()
|
|
734
|
+
if not mempool:
|
|
735
|
+
mempool = self.blockchain_manager.get_mempool()
|
|
736
|
+
return mempool if mempool else []
|
|
737
|
+
except Exception as e:
|
|
738
|
+
safe_print(f"Error fetching mempool: {e}")
|
|
739
|
+
return []
|
|
740
|
+
|
|
741
|
+
def _validate_transactions(self, transactions: List[Dict]) -> List[Dict]:
|
|
742
|
+
"""Validate transactions using security manager"""
|
|
743
|
+
valid_transactions = []
|
|
744
|
+
|
|
745
|
+
for tx in transactions:
|
|
746
|
+
try:
|
|
747
|
+
# Basic validation
|
|
748
|
+
if not self._validate_transaction_structure(tx):
|
|
749
|
+
continue
|
|
750
|
+
|
|
751
|
+
# Security validation if available
|
|
752
|
+
if self.transaction_validator:
|
|
753
|
+
if not self.transaction_validator.validate_transaction(tx):
|
|
754
|
+
continue
|
|
755
|
+
|
|
756
|
+
valid_transactions.append(tx)
|
|
757
|
+
except Exception as e:
|
|
758
|
+
safe_print(f"Transaction validation error: {e}")
|
|
759
|
+
continue
|
|
760
|
+
|
|
761
|
+
return valid_transactions
|
|
762
|
+
|
|
763
|
+
def _validate_transaction_structure(self, tx: Dict) -> bool:
|
|
764
|
+
"""Basic transaction structure validation"""
|
|
765
|
+
required_fields = ['type', 'timestamp']
|
|
766
|
+
|
|
767
|
+
for field in required_fields:
|
|
768
|
+
if field not in tx:
|
|
769
|
+
return False
|
|
770
|
+
|
|
771
|
+
tx_type = tx.get('type')
|
|
772
|
+
|
|
773
|
+
if tx_type == 'transaction':
|
|
774
|
+
if not all(k in tx for k in ['from', 'to', 'amount']):
|
|
775
|
+
return False
|
|
776
|
+
elif tx_type == 'genesis_bill':
|
|
777
|
+
if 'denomination' not in tx:
|
|
778
|
+
return False
|
|
779
|
+
elif tx_type == 'reward':
|
|
780
|
+
if not all(k in tx for k in ['to', 'amount']):
|
|
781
|
+
return False
|
|
782
|
+
|
|
783
|
+
return True
|
|
784
|
+
|
|
785
|
+
def _calculate_block_difficulty(self, transactions: List[Dict]) -> int:
|
|
786
|
+
"""Calculate block difficulty based on transactions using DifficultySystem"""
|
|
787
|
+
if not transactions:
|
|
788
|
+
return self.config.difficulty
|
|
789
|
+
|
|
790
|
+
max_difficulty = self.config.difficulty
|
|
791
|
+
|
|
792
|
+
for tx in transactions:
|
|
793
|
+
tx_type = tx.get('type')
|
|
794
|
+
|
|
795
|
+
if tx_type == 'genesis_bill':
|
|
796
|
+
denomination = tx.get('denomination', 0)
|
|
797
|
+
tx_difficulty = self.difficulty_system.get_bill_difficulty(denomination)
|
|
798
|
+
max_difficulty = max(max_difficulty, tx_difficulty)
|
|
799
|
+
elif tx_type == 'transaction':
|
|
800
|
+
amount = tx.get('amount', 0)
|
|
801
|
+
tx_difficulty = self.difficulty_system.get_transaction_difficulty(amount)
|
|
802
|
+
max_difficulty = max(max_difficulty, tx_difficulty)
|
|
803
|
+
elif tx_type == 'reward':
|
|
804
|
+
max_difficulty = max(max_difficulty, 1)
|
|
805
|
+
|
|
806
|
+
return max(max_difficulty, self.config.difficulty)
|
|
807
|
+
|
|
808
|
+
def _calculate_block_reward(self, transactions: List[Dict]) -> float:
|
|
809
|
+
"""Calculate total block reward based on transactions using DifficultySystem"""
|
|
810
|
+
total_reward = 0.0
|
|
811
|
+
|
|
812
|
+
for tx in transactions:
|
|
813
|
+
tx_type = tx.get('type')
|
|
814
|
+
|
|
815
|
+
if tx_type == 'genesis_bill':
|
|
816
|
+
denomination = tx.get('denomination', 0)
|
|
817
|
+
# Use difficulty system to calculate proper reward
|
|
818
|
+
bill_difficulty = self.difficulty_system.get_bill_difficulty(denomination)
|
|
819
|
+
avg_mining_time = 15.0 # Will be updated with actual time
|
|
820
|
+
bill_reward = self.difficulty_system.calculate_mining_reward(denomination, avg_mining_time)
|
|
821
|
+
total_reward += bill_reward
|
|
822
|
+
elif tx_type == 'transaction':
|
|
823
|
+
fee = tx.get('fee', 0)
|
|
824
|
+
total_reward += fee
|
|
825
|
+
elif tx_type == 'reward':
|
|
826
|
+
reward_amount = tx.get('amount', 0)
|
|
827
|
+
total_reward += reward_amount
|
|
828
|
+
|
|
829
|
+
# Minimum reward for empty blocks
|
|
830
|
+
if total_reward == 0:
|
|
831
|
+
total_reward = 1.0
|
|
832
|
+
|
|
833
|
+
return total_reward
|
|
834
|
+
|
|
835
|
+
def _calculate_exponential_block_reward(self, difficulty: int) -> float:
|
|
836
|
+
"""Calculate block reward using exponential difficulty system
|
|
837
|
+
|
|
838
|
+
Uses the new exponential reward system:
|
|
839
|
+
difficulty 1 = 1 LKC
|
|
840
|
+
difficulty 2 = 10 LKC
|
|
841
|
+
difficulty 3 = 100 LKC
|
|
842
|
+
difficulty 9 = 100,000,000 LKC
|
|
843
|
+
"""
|
|
844
|
+
return self.difficulty_system.calculate_block_reward(difficulty)
|
|
845
|
+
|
|
846
|
+
def _create_empty_block_reward(self, block_index: int) -> Dict:
|
|
847
|
+
"""Create reward transaction for empty blocks"""
|
|
848
|
+
return {
|
|
849
|
+
'type': 'reward',
|
|
850
|
+
'from': 'network',
|
|
851
|
+
'to': self.config.miner_address,
|
|
852
|
+
'amount': 1.0,
|
|
853
|
+
'timestamp': time.time(),
|
|
854
|
+
'block_height': block_index,
|
|
855
|
+
'hash': f"reward_{block_index}_{int(time.time())}",
|
|
856
|
+
'description': 'Empty block mining reward'
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
def _cuda_mine(self, block_data: Dict, difficulty: int) -> Optional[Dict]:
|
|
860
|
+
"""Mine using CUDA acceleration"""
|
|
861
|
+
try:
|
|
862
|
+
safe_print("Attempting CUDA mining...")
|
|
863
|
+
cuda_result = self.cuda_manager.cuda_mine_batch(
|
|
864
|
+
block_data, difficulty, batch_size=100000
|
|
865
|
+
)
|
|
866
|
+
if cuda_result and cuda_result.get('success'):
|
|
867
|
+
block_data['hash'] = cuda_result['hash']
|
|
868
|
+
block_data['nonce'] = cuda_result['nonce']
|
|
869
|
+
return block_data
|
|
870
|
+
except Exception as e:
|
|
871
|
+
safe_print(f"CUDA mining failed: {e}")
|
|
872
|
+
return None
|
|
873
|
+
|
|
874
|
+
def _cpu_mine(self, block_data: Dict, difficulty: int) -> Optional[Dict]:
|
|
875
|
+
"""Mine using CPU"""
|
|
876
|
+
safe_print("Using CPU mining...")
|
|
877
|
+
start_time = time.time()
|
|
878
|
+
target = "0" * difficulty
|
|
879
|
+
nonce = 0
|
|
880
|
+
hash_count = 0
|
|
881
|
+
last_hash_update = start_time
|
|
882
|
+
|
|
883
|
+
while not self.should_stop_mining and nonce < 1000000:
|
|
884
|
+
# Calculate block hash
|
|
885
|
+
block_hash = self._calculate_block_hash(
|
|
886
|
+
block_data['index'],
|
|
887
|
+
block_data['previous_hash'],
|
|
888
|
+
block_data['timestamp'],
|
|
889
|
+
block_data['transactions'],
|
|
890
|
+
nonce,
|
|
891
|
+
block_data['miner'],
|
|
892
|
+
difficulty
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
if block_hash.startswith(target):
|
|
896
|
+
block_data['hash'] = block_hash
|
|
897
|
+
block_data['nonce'] = nonce
|
|
898
|
+
return block_data
|
|
899
|
+
|
|
900
|
+
nonce += 1
|
|
901
|
+
hash_count += 1
|
|
902
|
+
self.current_nonce = nonce
|
|
903
|
+
self.current_hash = block_hash
|
|
904
|
+
|
|
905
|
+
# Update hash rate
|
|
906
|
+
current_time = time.time()
|
|
907
|
+
if current_time - last_hash_update >= 1:
|
|
908
|
+
self.hash_rate = hash_count / (current_time - last_hash_update)
|
|
909
|
+
hash_count = 0
|
|
910
|
+
last_hash_update = current_time
|
|
911
|
+
|
|
912
|
+
if nonce % 1000 == 0 and not self.is_mining:
|
|
913
|
+
return None
|
|
914
|
+
|
|
915
|
+
return None
|
|
916
|
+
|
|
917
|
+
def _calculate_block_hash(self, index: int, previous_hash: str, timestamp: float,
|
|
918
|
+
transactions: List[Dict], nonce: int, miner: str, difficulty: int) -> str:
|
|
919
|
+
"""Calculate SHA-256 hash of a block matching server validation"""
|
|
920
|
+
try:
|
|
921
|
+
block_data = {
|
|
922
|
+
"difficulty": int(difficulty),
|
|
923
|
+
"index": int(index),
|
|
924
|
+
"miner": str(miner),
|
|
925
|
+
"nonce": int(nonce),
|
|
926
|
+
"previous_hash": str(previous_hash),
|
|
927
|
+
"timestamp": float(timestamp),
|
|
928
|
+
"transactions": [], # Empty for mining proof
|
|
929
|
+
"version": "1.0"
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
block_string = json.dumps(block_data, sort_keys=True)
|
|
933
|
+
calculated_hash = hashlib.sha256(block_string.encode()).hexdigest()
|
|
934
|
+
return calculated_hash
|
|
935
|
+
|
|
936
|
+
except Exception as e:
|
|
937
|
+
safe_print(f"Hash calculation error: {e}")
|
|
938
|
+
return "0" * 64
|
|
939
|
+
|
|
940
|
+
def _finalize_block(self, block_data: Dict, method: str, total_reward: float) -> tuple[bool, str, Dict]:
|
|
941
|
+
"""Finalize mined block with proper record keeping and blockchain submission"""
|
|
942
|
+
mining_time = time.time() - block_data.get('timestamp', time.time())
|
|
943
|
+
|
|
944
|
+
# Validate block before submission
|
|
945
|
+
validation_result = self._validate_mined_block(block_data)
|
|
946
|
+
if not validation_result[0]:
|
|
947
|
+
safe_print(f"❌ Block validation failed: {validation_result[1]}")
|
|
948
|
+
return False, f"Block validation failed: {validation_result[1]}", None
|
|
949
|
+
|
|
950
|
+
# Block reward is already calculated based on difficulty (exponential system)
|
|
951
|
+
final_reward = block_data['reward']
|
|
952
|
+
|
|
953
|
+
# Submit block to blockchain
|
|
954
|
+
try:
|
|
955
|
+
submission_success = self.blockchain_manager.submit_mined_block(block_data)
|
|
956
|
+
|
|
957
|
+
if not submission_success:
|
|
958
|
+
safe_print(f"⚠️ Block #{block_data['index']} submission failed")
|
|
959
|
+
return False, f"Block #{block_data['index']} mined but submission failed", None
|
|
960
|
+
|
|
961
|
+
safe_print(f"✅ Block #{block_data['index']} submitted successfully (Reward: {final_reward} LKC)")
|
|
962
|
+
|
|
963
|
+
# Clear mined transactions from mempool
|
|
964
|
+
self._clear_transactions_from_mempool(block_data['transactions'])
|
|
965
|
+
|
|
966
|
+
except Exception as e:
|
|
967
|
+
safe_print(f"❌ Block submission error: {e}")
|
|
968
|
+
return False, f"Block submission error: {str(e)}", None
|
|
969
|
+
|
|
970
|
+
# Record mining history
|
|
971
|
+
mining_record = {
|
|
972
|
+
'block_index': block_data['index'],
|
|
973
|
+
'timestamp': time.time(),
|
|
974
|
+
'mining_time': mining_time,
|
|
975
|
+
'difficulty': block_data['difficulty'],
|
|
976
|
+
'nonce': block_data['nonce'],
|
|
977
|
+
'hash': block_data['hash'],
|
|
978
|
+
'method': method,
|
|
979
|
+
'reward': final_reward,
|
|
980
|
+
'status': 'success'
|
|
981
|
+
}
|
|
982
|
+
self.mining_history.append(mining_record)
|
|
983
|
+
self.save_mining_history()
|
|
984
|
+
|
|
985
|
+
self.blocks_mined += 1
|
|
986
|
+
self.total_reward += final_reward
|
|
987
|
+
|
|
988
|
+
if self.mining_completed_callback:
|
|
989
|
+
self.mining_completed_callback(True, f"Block #{block_data['index']} mined - Reward: {final_reward}")
|
|
990
|
+
|
|
991
|
+
if self.block_mined_callback:
|
|
992
|
+
self.block_mined_callback(block_data)
|
|
993
|
+
|
|
994
|
+
return True, f"Block #{block_data['index']} mined - Reward: {final_reward}", block_data
|
|
995
|
+
|
|
996
|
+
def _validate_mined_block(self, block: Dict) -> tuple:
|
|
997
|
+
"""Validate mined block before submission
|
|
998
|
+
|
|
999
|
+
Returns: (is_valid, error_message)
|
|
1000
|
+
"""
|
|
1001
|
+
# Validate structure
|
|
1002
|
+
is_valid, error = self.difficulty_system.validate_block_structure(block)
|
|
1003
|
+
if not is_valid:
|
|
1004
|
+
return False, error
|
|
1005
|
+
|
|
1006
|
+
# Validate hash meets difficulty
|
|
1007
|
+
block_hash = block.get('hash', '')
|
|
1008
|
+
difficulty = block.get('difficulty', 0)
|
|
1009
|
+
|
|
1010
|
+
if not self.difficulty_system.validate_block_hash(block_hash, difficulty):
|
|
1011
|
+
return False, f"Hash does not meet difficulty {difficulty} requirement"
|
|
1012
|
+
|
|
1013
|
+
# Validate previous hash (get from blockchain)
|
|
1014
|
+
try:
|
|
1015
|
+
latest_block = self.blockchain_manager.get_latest_block()
|
|
1016
|
+
if latest_block:
|
|
1017
|
+
expected_prev_hash = latest_block.get('hash', '')
|
|
1018
|
+
if block.get('previous_hash') != expected_prev_hash:
|
|
1019
|
+
return False, f"Previous hash mismatch: expected {expected_prev_hash[:16]}..., got {block.get('previous_hash', '')[:16]}..."
|
|
1020
|
+
except Exception as e:
|
|
1021
|
+
safe_print(f"⚠️ Could not validate previous hash: {e}")
|
|
1022
|
+
|
|
1023
|
+
# Validate reward matches difficulty
|
|
1024
|
+
expected_reward = self.difficulty_system.calculate_block_reward(difficulty)
|
|
1025
|
+
actual_reward = block.get('reward', 0)
|
|
1026
|
+
|
|
1027
|
+
# Allow some tolerance for floating point comparison
|
|
1028
|
+
if abs(actual_reward - expected_reward) > 0.01:
|
|
1029
|
+
return False, f"Reward mismatch: expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC"
|
|
1030
|
+
|
|
1031
|
+
safe_print(f"✅ Block validation passed: Hash meets difficulty {difficulty}, Reward: {actual_reward} LKC")
|
|
1032
|
+
return True, ""
|
|
1033
|
+
|
|
1034
|
+
def _clear_transactions_from_mempool(self, transactions: List[Dict]):
|
|
1035
|
+
"""Remove mined transactions from mempool"""
|
|
1036
|
+
try:
|
|
1037
|
+
for tx in transactions:
|
|
1038
|
+
# Skip reward transactions (they were created during mining)
|
|
1039
|
+
if tx.get('type') == 'reward' and tx.get('from') == 'network':
|
|
1040
|
+
continue
|
|
1041
|
+
|
|
1042
|
+
tx_hash = tx.get('hash')
|
|
1043
|
+
if tx_hash:
|
|
1044
|
+
# Remove from mempool manager if available
|
|
1045
|
+
try:
|
|
1046
|
+
self.mempool_manager.remove_transaction(tx_hash)
|
|
1047
|
+
except:
|
|
1048
|
+
pass # Silent fail if method doesn't exist
|
|
1049
|
+
|
|
1050
|
+
safe_print(f"🧹 Cleared {len(transactions)} transactions from mempool")
|
|
1051
|
+
|
|
1052
|
+
except Exception as e:
|
|
1053
|
+
safe_print(f"⚠️ Error clearing mempool: {e}")
|
|
1054
|
+
|
|
1055
|
+
def _calculate_final_reward(self, transactions: List[Dict], actual_mining_time: float) -> float:
|
|
1056
|
+
"""Calculate final reward using actual mining time"""
|
|
1057
|
+
total_reward = 0.0
|
|
1058
|
+
|
|
1059
|
+
for tx in transactions:
|
|
1060
|
+
tx_type = tx.get('type')
|
|
1061
|
+
|
|
1062
|
+
if tx_type == 'genesis_bill':
|
|
1063
|
+
denomination = tx.get('denomination', 0)
|
|
1064
|
+
bill_reward = self.difficulty_system.calculate_mining_reward(denomination, actual_mining_time)
|
|
1065
|
+
total_reward += bill_reward
|
|
1066
|
+
elif tx_type == 'transaction':
|
|
1067
|
+
total_reward += tx.get('fee', 0)
|
|
1068
|
+
elif tx_type == 'reward':
|
|
1069
|
+
total_reward += tx.get('amount', 0)
|
|
1070
|
+
|
|
1071
|
+
if total_reward == 0:
|
|
1072
|
+
total_reward = 1.0
|
|
1073
|
+
|
|
1074
|
+
return total_reward
|
|
1075
|
+
|
|
1076
|
+
def save_mining_history(self):
|
|
1077
|
+
"""Save mining history to storage"""
|
|
1078
|
+
self.data_manager.save_mining_history(self.mining_history)
|
|
1079
|
+
|
|
1080
|
+
def start_mining(self):
|
|
1081
|
+
"""Start the mining process"""
|
|
1082
|
+
if self.is_mining:
|
|
1083
|
+
return
|
|
1084
|
+
|
|
1085
|
+
self.is_mining = True
|
|
1086
|
+
self.should_stop_mining = False
|
|
1087
|
+
|
|
1088
|
+
if self.mining_started_callback:
|
|
1089
|
+
self.mining_started_callback()
|
|
1090
|
+
|
|
1091
|
+
def stop_mining(self):
|
|
1092
|
+
"""Stop the mining process"""
|
|
1093
|
+
self.is_mining = False
|
|
1094
|
+
self.should_stop_mining = True
|
|
1095
|
+
if self.mining_thread and self.mining_thread.is_alive():
|
|
1096
|
+
self.mining_thread.join()
|
|
1097
|
+
|
|
1098
|
+
def get_mining_stats(self):
|
|
1099
|
+
"""Return the current mining statistics"""
|
|
1100
|
+
return {
|
|
1101
|
+
"blocks_mined": self.blocks_mined,
|
|
1102
|
+
"total_reward": self.total_reward,
|
|
1103
|
+
"current_hash": self.current_hash,
|
|
1104
|
+
"current_nonce": self.current_nonce,
|
|
1105
|
+
"hash_rate": self.hash_rate,
|
|
1106
|
+
"mining_history": len(self.mining_history)
|
|
1107
|
+
}
|
lunalib/transactions/security.py
CHANGED
|
@@ -123,22 +123,22 @@ class TransactionSecurity:
|
|
|
123
123
|
|
|
124
124
|
# For unsigned test transactions
|
|
125
125
|
if signature in ["system", "unsigned", "test"]:
|
|
126
|
-
|
|
126
|
+
safe_print(f"[SECURITY] Skipping signature check for system/unsigned transaction")
|
|
127
127
|
return True
|
|
128
128
|
|
|
129
129
|
# Check SM2 signature length (should be 128 hex chars = 64 bytes)
|
|
130
130
|
if len(signature) != 128:
|
|
131
|
-
|
|
131
|
+
safe_print(f"[SECURITY] Invalid SM2 signature length: {len(signature)} (expected 128)")
|
|
132
132
|
return False
|
|
133
133
|
|
|
134
134
|
# Check if all characters are valid hex
|
|
135
135
|
if not all(c in "0123456789abcdefABCDEF" for c in signature):
|
|
136
|
-
|
|
136
|
+
safe_print(f"[SECURITY] Signature contains non-hex characters")
|
|
137
137
|
return False
|
|
138
138
|
|
|
139
139
|
# Check public key format (should start with '04' for uncompressed)
|
|
140
140
|
if not public_key.startswith('04'):
|
|
141
|
-
|
|
141
|
+
safe_print(f"[SECURITY] Invalid public key format: {public_key[:20]}...")
|
|
142
142
|
return False
|
|
143
143
|
|
|
144
144
|
# Use KeyManager for verification if available
|
|
@@ -148,15 +148,15 @@ class TransactionSecurity:
|
|
|
148
148
|
|
|
149
149
|
# Verify signature
|
|
150
150
|
is_valid = self.key_manager.verify_signature(signing_data, signature, public_key)
|
|
151
|
-
|
|
151
|
+
safe_print(f"[SECURITY] SM2 signature verification: {is_valid}")
|
|
152
152
|
return is_valid
|
|
153
153
|
|
|
154
154
|
# Fallback: Basic format check if SM2 not available
|
|
155
|
-
|
|
155
|
+
safe_print(f"[SECURITY] SM2 not available, using basic signature validation")
|
|
156
156
|
return len(signature) == 128 and signature.startswith(('04', '03', '02'))
|
|
157
157
|
|
|
158
158
|
except Exception as e:
|
|
159
|
-
|
|
159
|
+
safe_print(f"[SECURITY] Signature validation error: {e}")
|
|
160
160
|
return False
|
|
161
161
|
|
|
162
162
|
def _get_signing_data(self, transaction: Dict) -> str:
|
|
@@ -183,7 +183,7 @@ class TransactionSecurity:
|
|
|
183
183
|
|
|
184
184
|
def _validate_signature(self, transaction: Dict) -> bool:
|
|
185
185
|
"""Legacy signature validation (for backward compatibility)"""
|
|
186
|
-
|
|
186
|
+
safe_print(f"[SECURITY] Using legacy signature validation")
|
|
187
187
|
return self._validate_signature_sm2(transaction)
|
|
188
188
|
|
|
189
189
|
def _check_rate_limit(self, address: str) -> bool:
|