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.
- lunalib/core/__init__.py +14 -0
- lunalib/core/blockchain.py +183 -3
- lunalib/core/daemon.py +494 -0
- lunalib/core/p2p.py +361 -0
- lunalib/core/sm2.py +723 -723
- lunalib/core/wallet.py +714 -478
- lunalib/core/wallet_manager.py +638 -638
- lunalib/core/wallet_sync_helper.py +163 -163
- 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 +526 -17
- lunalib/storage/cache.py +13 -4
- lunalib/storage/database.py +14 -5
- lunalib/storage/encryption.py +11 -2
- lunalib/transactions/security.py +19 -10
- lunalib/transactions/transactions.py +50 -41
- lunalib-1.7.2.dist-info/METADATA +27 -0
- lunalib-1.7.2.dist-info/RECORD +33 -0
- lunalib-1.7.2.dist-info/top_level.txt +1 -0
- core/__init__.py +0 -0
- core/blockchain.py +0 -172
- core/crypto.py +0 -32
- core/wallet.py +0 -408
- gtx/__init__.py +0 -0
- gtx/bill_registry.py +0 -122
- gtx/digital_bill.py +0 -273
- gtx/genesis.py +0 -338
- lunalib/requirements.txt +0 -44
- lunalib-1.5.1.dist-info/METADATA +0 -283
- lunalib-1.5.1.dist-info/RECORD +0 -53
- lunalib-1.5.1.dist-info/entry_points.txt +0 -2
- lunalib-1.5.1.dist-info/top_level.txt +0 -6
- mining/__init__.py +0 -0
- mining/cuda_manager.py +0 -137
- mining/difficulty.py +0 -106
- mining/miner.py +0 -107
- storage/__init__.py +0 -0
- storage/cache.py +0 -148
- storage/database.py +0 -222
- storage/encryption.py +0 -105
- transactions/__init__.py +0 -0
- transactions/security.py +0 -172
- transactions/transactions.py +0 -424
- transactions/validator.py +0 -71
- {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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
157
|
+
safe_print(f"Cache cleanup error: {e}")
|