lunalib 1.2.3__py3-none-any.whl → 1.5.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.
@@ -0,0 +1,163 @@
1
+ # lunalib/wallet_sync_helper.py
2
+ """
3
+ Wallet Sync Helper
4
+
5
+ Provides integration between LunaWallet, BlockchainManager, MempoolManager,
6
+ and the WalletStateManager for seamless real-time balance and transaction updates.
7
+ """
8
+
9
+ from typing import List, Dict, Optional, Callable
10
+ from .wallet_manager import get_wallet_manager
11
+
12
+
13
+ class WalletSyncHelper:
14
+ """
15
+ Helper class to sync LunaWallet with WalletStateManager using data from
16
+ BlockchainManager and MempoolManager.
17
+ """
18
+
19
+ def __init__(self, wallet=None, blockchain=None, mempool=None):
20
+ """
21
+ Initialize sync helper.
22
+
23
+ Parameters:
24
+ wallet: LunaWallet instance
25
+ blockchain: BlockchainManager instance
26
+ mempool: MempoolManager instance
27
+ """
28
+ self.wallet = wallet
29
+ self.blockchain = blockchain
30
+ self.mempool = mempool
31
+ self.state_manager = get_wallet_manager()
32
+
33
+ def register_wallets_from_lunawallet(self) -> Dict:
34
+ """Register all wallets from LunaWallet into the state manager"""
35
+ if not self.wallet:
36
+ print("⚠️ No wallet instance provided")
37
+ return {}
38
+
39
+ addresses = list(self.wallet.wallets.keys())
40
+ if not addresses:
41
+ print("⚠️ No wallets registered in LunaWallet")
42
+ return {}
43
+
44
+ print(f"📱 Registering {len(addresses)} wallets with state manager...")
45
+ states = self.state_manager.register_wallets(addresses)
46
+ print(f"✅ Registered {len(states)} wallets")
47
+
48
+ return states
49
+
50
+ def sync_wallets_now(self) -> Dict:
51
+ """
52
+ Perform a single synchronization of all registered wallets.
53
+
54
+ Returns: Dictionary of wallet addresses and their updated summaries
55
+ """
56
+ if not self.blockchain or not self.mempool:
57
+ print("⚠️ Blockchain or mempool not provided")
58
+ return {}
59
+
60
+ addresses = list(self.state_manager.wallet_states.keys())
61
+ if not addresses:
62
+ print("⚠️ No wallets registered")
63
+ return {}
64
+
65
+ print(f"🔄 Syncing {len(addresses)} wallets...")
66
+
67
+ try:
68
+ # Get data from blockchain and mempool
69
+ blockchain_txs = self.blockchain.scan_transactions_for_addresses(addresses)
70
+ mempool_txs = self.mempool.get_pending_transactions_for_addresses(addresses)
71
+
72
+ # Sync the state manager
73
+ self.state_manager.sync_wallets_from_sources(blockchain_txs, mempool_txs)
74
+
75
+ # Update LunaWallet balances if available
76
+ if self.wallet:
77
+ self._update_lunawallet_balances()
78
+
79
+ # Return summaries
80
+ return self.state_manager.get_all_summaries()
81
+
82
+ except Exception as e:
83
+ print(f"❌ Sync error: {e}")
84
+ return {}
85
+
86
+ def _update_lunawallet_balances(self):
87
+ """Update LunaWallet instance balances from state manager"""
88
+ if not self.wallet:
89
+ return
90
+
91
+ balances = self.state_manager.get_all_balances()
92
+
93
+ for address, balance_data in balances.items():
94
+ if address in self.wallet.wallets:
95
+ wallet_data = self.wallet.wallets[address]
96
+ wallet_data['balance'] = balance_data['confirmed_balance']
97
+ wallet_data['available_balance'] = balance_data['available_balance']
98
+
99
+ # Update current wallet if one is selected
100
+ if self.wallet.current_wallet_address in balances:
101
+ balance_data = balances[self.wallet.current_wallet_address]
102
+ self.wallet.balance = balance_data['confirmed_balance']
103
+ self.wallet.available_balance = balance_data['available_balance']
104
+
105
+ def start_continuous_sync(self, poll_interval: int = 30,
106
+ on_balance_update: Optional[Callable] = None,
107
+ on_transaction_update: Optional[Callable] = None) -> None:
108
+ """
109
+ Start continuous synchronization in the background.
110
+
111
+ Parameters:
112
+ poll_interval: Seconds between syncs
113
+ on_balance_update: Callback function(balance_data) for balance updates
114
+ on_transaction_update: Callback function(transaction_data) for transaction updates
115
+ """
116
+
117
+ if on_balance_update:
118
+ self.state_manager.on_balance_update(on_balance_update)
119
+
120
+ if on_transaction_update:
121
+ self.state_manager.on_transaction_update(on_transaction_update)
122
+
123
+ def get_blockchain_data(addresses):
124
+ try:
125
+ return self.blockchain.scan_transactions_for_addresses(addresses)
126
+ except Exception as e:
127
+ print(f"⚠️ Blockchain scan error: {e}")
128
+ return {}
129
+
130
+ def get_mempool_data(addresses):
131
+ try:
132
+ return self.mempool.get_pending_transactions_for_addresses(addresses)
133
+ except Exception as e:
134
+ print(f"⚠️ Mempool fetch error: {e}")
135
+ return {}
136
+
137
+ self.state_manager.sync_wallets_background(
138
+ get_blockchain_data,
139
+ get_mempool_data,
140
+ poll_interval
141
+ )
142
+
143
+ def get_wallet_balance(self, address: str) -> Optional[Dict]:
144
+ """Get current balance for a wallet"""
145
+ return self.state_manager.get_balance(address)
146
+
147
+ def get_wallet_transactions(self, address: str, tx_type: str = 'all') -> List[Dict]:
148
+ """Get transactions for a wallet"""
149
+ return self.state_manager.get_transactions(address, tx_type)
150
+
151
+ def get_wallet_summary(self, address: str) -> Optional[Dict]:
152
+ """Get complete summary for a wallet"""
153
+ return self.state_manager.get_wallet_summary(address)
154
+
155
+ def get_all_wallets_summary(self) -> Dict:
156
+ """Get summaries for all wallets"""
157
+ return self.state_manager.get_all_summaries()
158
+
159
+
160
+ # Convenience function to create sync helper
161
+ def create_wallet_sync_helper(wallet=None, blockchain=None, mempool=None) -> WalletSyncHelper:
162
+ """Create a new WalletSyncHelper instance"""
163
+ return WalletSyncHelper(wallet, blockchain, mempool)
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
@@ -28,7 +37,7 @@ class GenesisMiner:
28
37
  "total_hash_attempts": 0
29
38
  }
30
39
 
31
- print("🔧 GenesisMiner initialized with integrated lunalib components")
40
+ safe_print("🔧 GenesisMiner initialized with integrated lunalib components")
32
41
 
33
42
  def mine_bill(self, denomination: float, user_address: str, bill_data: Dict = None) -> Dict:
34
43
  """Mine a GTX Genesis bill using DigitalBill system"""
@@ -43,7 +52,7 @@ class GenesisMiner:
43
52
  bill_data=bill_data or {}
44
53
  )
45
54
 
46
- print(f"⛏️ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
55
+ safe_print(f"⛏️ Mining GTX ${denomination:,} Bill - Difficulty: {difficulty} zeros")
47
56
 
48
57
  start_time = time.time()
49
58
  mining_result = self._perform_bill_mining(digital_bill, difficulty)
@@ -63,10 +72,10 @@ class GenesisMiner:
63
72
  self.mining_stats["total_mining_time"] += mining_time
64
73
  self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
65
74
 
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]}...")
75
+ safe_print(f"✅ Successfully mined GTX ${denomination:,} bill!")
76
+ safe_print(f"⏱️ Mining time: {mining_time:.2f}s")
77
+ safe_print(f"📊 Hash attempts: {mining_result['nonce']:,}")
78
+ safe_print(f"🔗 Bill hash: {mining_result['hash'][:32]}...")
70
79
 
71
80
  # Convert to GTX Genesis transaction
72
81
  gtx_transaction = self._create_gtx_genesis_transaction(bill)
@@ -105,8 +114,8 @@ class GenesisMiner:
105
114
  # Calculate block difficulty
106
115
  difficulty = self.difficulty_system.get_transaction_block_difficulty(transactions)
107
116
 
108
- print(f"⛏️ Mining Transaction Block #{block_height} - Difficulty: {difficulty} zeros")
109
- print(f"📦 Transactions: {len(transactions)} | Previous Hash: {previous_hash[:16]}...")
117
+ safe_print(f"⛏️ Mining Transaction Block #{block_height} - Difficulty: {difficulty} zeros")
118
+ safe_print(f"📦 Transactions: {len(transactions)} | Previous Hash: {previous_hash[:16]}...")
110
119
 
111
120
  # Create block structure for mining
112
121
  block_data = {
@@ -162,20 +171,20 @@ class GenesisMiner:
162
171
  self.mining_stats["total_mining_time"] += mining_time
163
172
  self.mining_stats["total_hash_attempts"] += mining_result["nonce"]
164
173
 
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]}...")
174
+ safe_print(f"✅ Successfully mined and validated Transaction Block #{block_height}!")
175
+ safe_print(f"⏱️ Mining time: {mining_time:.2f}s")
176
+ safe_print(f"💰 Block reward: {block['reward']:.6f} LUN")
177
+ safe_print(f"📊 Transactions: {block['transaction_count']}")
178
+ safe_print(f"🔗 Block hash: {mining_result['hash'][:32]}...")
170
179
 
171
180
  # Submit block to blockchain
172
181
  submission_success = self.blockchain_manager.submit_mined_block(block)
173
182
  if submission_success:
174
- print("✅ Block successfully submitted to blockchain!")
183
+ safe_print("✅ Block successfully submitted to blockchain!")
175
184
  # Clear mined transactions from local mempool
176
185
  self._clear_mined_transactions(transactions)
177
186
  else:
178
- print("⚠️ Block mined but submission failed")
187
+ safe_print("⚠️ Block mined but submission failed")
179
188
 
180
189
  return {
181
190
  "success": True,
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}")
@@ -3,6 +3,15 @@ import sqlite3
3
3
  import json
4
4
  import time
5
5
  from typing import Dict, List, Optional, Any
6
+ import sys
7
+
8
+ # --- Unicode-safe print for Windows console ---
9
+ def safe_print(*args, **kwargs):
10
+ try:
11
+ print(*args, **kwargs)
12
+ except UnicodeEncodeError:
13
+ encoding = getattr(sys.stdout, 'encoding', 'utf-8')
14
+ print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
6
15
 
7
16
  class WalletDatabase:
8
17
  """Manages wallet data storage"""
@@ -96,7 +105,7 @@ class WalletDatabase:
96
105
  return True
97
106
 
98
107
  except Exception as e:
99
- print(f"Save wallet error: {e}")
108
+ safe_print(f"Save wallet error: {e}")
100
109
  return False
101
110
 
102
111
  def load_wallet(self, address: str) -> Optional[Dict]:
@@ -122,7 +131,7 @@ class WalletDatabase:
122
131
  }
123
132
 
124
133
  except Exception as e:
125
- print(f"Load wallet error: {e}")
134
+ safe_print(f"Load wallet error: {e}")
126
135
 
127
136
  return None
128
137
 
@@ -157,7 +166,7 @@ class WalletDatabase:
157
166
  return True
158
167
 
159
168
  except Exception as e:
160
- print(f"Save transaction error: {e}")
169
+ safe_print(f"Save transaction error: {e}")
161
170
  return False
162
171
 
163
172
  def get_wallet_transactions(self, wallet_address: str, limit: int = 100) -> List[Dict]:
@@ -187,7 +196,7 @@ class WalletDatabase:
187
196
  return transactions
188
197
 
189
198
  except Exception as e:
190
- print(f"Get transactions error: {e}")
199
+ safe_print(f"Get transactions error: {e}")
191
200
  return []
192
201
 
193
202
  def save_pending_transaction(self, transaction: Dict, wallet_address: str) -> bool:
@@ -218,5 +227,5 @@ class WalletDatabase:
218
227
  return True
219
228
 
220
229
  except Exception as e:
221
- print(f"Save pending transaction error: {e}")
230
+ safe_print(f"Save pending transaction error: {e}")
222
231
  return False
@@ -1,5 +1,14 @@
1
1
  import os
2
2
  import json
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 base64
4
13
  import hashlib
5
14
  from cryptography.fernet import Fernet
@@ -37,7 +46,7 @@ class EncryptionManager:
37
46
  }
38
47
 
39
48
  except Exception as e:
40
- print(f"Encryption error: {e}")
49
+ safe_print(f"Encryption error: {e}")
41
50
  return {}
42
51
 
43
52
  def decrypt_wallet(self, encrypted_data: Dict, password: str) -> Optional[Dict]:
@@ -65,7 +74,7 @@ class EncryptionManager:
65
74
  return wallet_data
66
75
 
67
76
  except Exception as e:
68
- print(f"Decryption error: {e}")
77
+ safe_print(f"Decryption error: {e}")
69
78
  return None
70
79
 
71
80
  def _derive_key(self, password: str) -> bytes:
@@ -1,9 +1,18 @@
1
1
  import time
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 hashlib
3
12
  from typing import Dict, Tuple
4
13
 
5
14
  class TransactionSecurity:
6
- """Enhanced transaction security system"""
15
+ """Enhanced transaction security system with SM2 support"""
7
16
 
8
17
  def __init__(self):
9
18
  self.min_transaction_amount = 0.000001
@@ -11,9 +20,20 @@ class TransactionSecurity:
11
20
  self.required_fee = 0.00001
12
21
  self.rate_limits = {}
13
22
  self.blacklisted_addresses = set()
23
+
24
+ # Try to import SM2 KeyManager
25
+ try:
26
+ from ..core.crypto import KeyManager as SM2KeyManager
27
+ self.key_manager = SM2KeyManager()
28
+ self.sm2_available = True
29
+ safe_print("[SECURITY] SM2 KeyManager loaded successfully")
30
+ except ImportError as e:
31
+ self.key_manager = None
32
+ self.sm2_available = False
33
+ safe_print(f"[SECURITY] SM2 KeyManager not available: {e}")
14
34
 
15
35
  def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
16
- """Comprehensive transaction security validation"""
36
+ """Comprehensive transaction security validation with SM2"""
17
37
  tx_type = transaction.get("type", "").lower()
18
38
 
19
39
  if tx_type == "gtx_genesis":
@@ -58,7 +78,7 @@ class TransactionSecurity:
58
78
  return True, "Valid reward transaction"
59
79
 
60
80
  def _validate_transfer_transaction(self, transaction: Dict) -> Tuple[bool, str]:
61
- """Validate transfer transaction"""
81
+ """Validate transfer transaction with SM2 signature"""
62
82
  required_fields = ["from", "to", "amount", "signature", "public_key", "nonce"]
63
83
  for field in required_fields:
64
84
  if field not in transaction:
@@ -76,9 +96,9 @@ class TransactionSecurity:
76
96
  if fee < self.required_fee:
77
97
  return False, f"Insufficient fee: {fee} (required: {self.required_fee})"
78
98
 
79
- # Signature validation
80
- if not self._validate_signature(transaction):
81
- return False, "Invalid signature"
99
+ # SM2 Signature validation
100
+ if not self._validate_signature_sm2(transaction):
101
+ return False, "Invalid SM2 signature"
82
102
 
83
103
  # Anti-spam checks
84
104
  from_address = transaction.get("from", "")
@@ -90,6 +110,65 @@ class TransactionSecurity:
90
110
 
91
111
  return True, "Valid transfer transaction"
92
112
 
113
+ def _validate_signature_sm2(self, transaction: Dict) -> bool:
114
+ """Validate transaction signature using SM2"""
115
+ try:
116
+ signature = transaction.get("signature", "")
117
+ public_key = transaction.get("public_key", "")
118
+ tx_type = transaction.get("type", "").lower()
119
+
120
+ # Skip system transactions
121
+ if tx_type in ["gtx_genesis", "reward"]:
122
+ return True
123
+
124
+ # For unsigned test transactions
125
+ if signature in ["system", "unsigned", "test"]:
126
+ print(f"[SECURITY] Skipping signature check for system/unsigned transaction")
127
+ return True
128
+
129
+ # Check SM2 signature length (should be 128 hex chars = 64 bytes)
130
+ if len(signature) != 128:
131
+ print(f"[SECURITY] Invalid SM2 signature length: {len(signature)} (expected 128)")
132
+ return False
133
+
134
+ # Check if all characters are valid hex
135
+ if not all(c in "0123456789abcdefABCDEF" for c in signature):
136
+ print(f"[SECURITY] Signature contains non-hex characters")
137
+ return False
138
+
139
+ # Check public key format (should start with '04' for uncompressed)
140
+ if not public_key.startswith('04'):
141
+ print(f"[SECURITY] Invalid public key format: {public_key[:20]}...")
142
+ return False
143
+
144
+ # Use KeyManager for verification if available
145
+ if self.sm2_available and self.key_manager:
146
+ # Create signing data string
147
+ signing_data = self._get_signing_data(transaction)
148
+
149
+ # Verify signature
150
+ is_valid = self.key_manager.verify_signature(signing_data, signature, public_key)
151
+ print(f"[SECURITY] SM2 signature verification: {is_valid}")
152
+ return is_valid
153
+
154
+ # Fallback: Basic format check if SM2 not available
155
+ print(f"[SECURITY] SM2 not available, using basic signature validation")
156
+ return len(signature) == 128 and signature.startswith(('04', '03', '02'))
157
+
158
+ except Exception as e:
159
+ print(f"[SECURITY] Signature validation error: {e}")
160
+ return False
161
+
162
+ def _get_signing_data(self, transaction: Dict) -> str:
163
+ """Create data string for signing"""
164
+ # Create copy without signature and hash
165
+ tx_copy = {k: v for k, v in transaction.items()
166
+ if k not in ['signature', 'hash']}
167
+
168
+ # Sort keys and convert to string
169
+ import json
170
+ return json.dumps(tx_copy, sort_keys=True)
171
+
93
172
  def _validate_mining_proof(self, transaction: Dict) -> bool:
94
173
  """Validate mining proof-of-work"""
95
174
  try:
@@ -103,20 +182,9 @@ class TransactionSecurity:
103
182
  return False
104
183
 
105
184
  def _validate_signature(self, transaction: Dict) -> bool:
106
- """Validate transaction signature"""
107
- try:
108
- signature = transaction.get("signature", "")
109
- public_key = transaction.get("public_key", "")
110
-
111
- # Basic format validation
112
- if len(signature) != 64:
113
- return False
114
-
115
- # In production, use proper ECDSA verification
116
- # For now, simplified check
117
- return all(c in "0123456789abcdef" for c in signature.lower())
118
- except:
119
- return False
185
+ """Legacy signature validation (for backward compatibility)"""
186
+ print(f"[SECURITY] Using legacy signature validation")
187
+ return self._validate_signature_sm2(transaction)
120
188
 
121
189
  def _check_rate_limit(self, address: str) -> bool:
122
190
  """Check transaction rate limiting"""
@@ -144,17 +212,20 @@ class TransactionSecurity:
144
212
  self.blacklisted_addresses.add(address.lower())
145
213
 
146
214
  def calculate_security_score(self, transaction: Dict) -> int:
147
- """Calculate security score for transaction"""
215
+ """Calculate security score for transaction with SM2 bonus"""
148
216
  score = 0
149
217
 
150
- # Signature strength
218
+ # Signature strength (SM2 gives higher score)
151
219
  signature = transaction.get("signature", "")
152
- if len(signature) == 64:
220
+ if len(signature) == 128: # SM2 signature
221
+ score += 60 # Higher score for SM2
222
+ elif len(signature) == 64: # Legacy ECDSA
153
223
  score += 40
154
224
 
155
225
  # Public key presence
156
- if transaction.get("public_key"):
157
- score += 20
226
+ public_key = transaction.get("public_key", "")
227
+ if public_key and public_key.startswith('04'):
228
+ score += 30 # SM2 uncompressed format
158
229
 
159
230
  # Timestamp freshness
160
231
  timestamp = transaction.get("timestamp", 0)