lunalib 1.5.0__py3-none-any.whl → 1.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)
@@ -1,3 +1,11 @@
1
+ import sys
2
+ def safe_print(*args, **kwargs):
3
+ encoding = sys.stdout.encoding or 'utf-8'
4
+ try:
5
+ print(*args, **kwargs)
6
+ except UnicodeEncodeError:
7
+ print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
8
+ safe_print("Warning: cryptography library not available. Using fallback methods.")
1
9
  import time
2
10
  import hashlib
3
11
  import secrets
@@ -12,7 +20,7 @@ try:
12
20
  from cryptography.exceptions import InvalidSignature
13
21
  CRYPTOGRAPHY_AVAILABLE = True
14
22
  except ImportError:
15
- print("Warning: cryptography library not available. Using fallback methods.")
23
+ safe_print("Warning: cryptography library not available. Using fallback methods.")
16
24
  CRYPTOGRAPHY_AVAILABLE = False
17
25
 
18
26
  from .bill_registry import BillRegistry
@@ -182,7 +190,7 @@ class DigitalBill:
182
190
 
183
191
  return self.signature
184
192
  except Exception as e:
185
- print(f"Cryptographic signing failed, using fallback: {e}")
193
+ safe_print(f"Cryptographic signing failed, using fallback: {e}")
186
194
  return self._sign_fallback(private_key)
187
195
 
188
196
  def _sign_fallback(self, private_key):
lunalib/gtx/genesis.py CHANGED
@@ -48,7 +48,7 @@ class GTXGenesis:
48
48
  return {'valid': False, 'error': 'Bill not found in registry'}
49
49
 
50
50
  # DEBUG: Print what we received
51
- print(f"DEBUG: Full bill record: {bill_record}")
51
+ safe_print(f"DEBUG: Full bill record: {bill_record}")
52
52
 
53
53
  # Extract the actual bill_data from the metadata field
54
54
  bill_data = bill_record.get('metadata', {})
@@ -56,7 +56,7 @@ class GTXGenesis:
56
56
  return {'valid': False, 'error': 'No bill data found in metadata'}
57
57
 
58
58
  # DEBUG: Print the extracted bill_data
59
- print(f"DEBUG: Extracted bill_data: {bill_data}")
59
+ safe_print(f"DEBUG: Extracted bill_data: {bill_data}")
60
60
 
61
61
  # Extract signature components from bill_data (not from bill_record)
62
62
  public_key = bill_data.get('public_key')
@@ -68,10 +68,10 @@ class GTXGenesis:
68
68
  timestamp = bill_data.get('timestamp', 0)
69
69
  bill_type = bill_data.get('type', 'GTX_Genesis')
70
70
 
71
- print(f"🔍 GTXGenesis.verify_bill() for {front_serial}:")
72
- print(f" Signature: {signature}")
73
- print(f" Public Key: {public_key}")
74
- print(f" Metadata Hash: {metadata_hash}")
71
+ safe_print(f"🔍 GTXGenesis.verify_bill() for {front_serial}:")
72
+ safe_print(f" Signature: {signature}")
73
+ safe_print(f" Public Key: {public_key}")
74
+ safe_print(f" Metadata Hash: {metadata_hash}")
75
75
 
76
76
  # Use the same verification logic as the endpoint
77
77
  verification_method = "unknown"
@@ -81,7 +81,7 @@ class GTXGenesis:
81
81
  if metadata_hash and signature == metadata_hash:
82
82
  signature_valid = True
83
83
  verification_method = "signature_is_metadata_hash"
84
- print(f"✅ Verified: signature matches metadata_hash")
84
+ safe_print(f"✅ Verified: signature matches metadata_hash")
85
85
 
86
86
  # METHOD 2: Check hash of public_key + metadata_hash
87
87
  elif signature_valid is None and metadata_hash and public_key and signature:
@@ -90,7 +90,7 @@ class GTXGenesis:
90
90
  if signature == expected_signature:
91
91
  signature_valid = True
92
92
  verification_method = "metadata_hash_signature"
93
- print(f"Verified: hash(public_key + metadata_hash)")
93
+ safe_print(f"Verified: hash(public_key + metadata_hash)")
94
94
 
95
95
  # METHOD 3: Check DigitalBill calculated hash
96
96
  elif signature_valid is None:
@@ -122,32 +122,31 @@ class GTXGenesis:
122
122
  if signature == calculated_hash:
123
123
  signature_valid = True
124
124
  verification_method = "digital_bill_calculate_hash"
125
- print(f"Verified: DigitalBill.calculate_hash()")
125
+ safe_print(f"Verified: DigitalBill.calculate_hash()")
126
126
  print(f" Calculated hash: {calculated_hash}")
127
127
 
128
128
  # Approach 2: Use the verify() method (checks all signature types)
129
129
  elif digital_bill.verify():
130
130
  signature_valid = True
131
131
  verification_method = "digital_bill_verify_method"
132
- print(f"Verified: DigitalBill.verify()")
132
+ safe_print(f"Verified: DigitalBill.verify()")
133
133
 
134
134
  # Approach 3: Check if signature matches metadata_hash generation
135
135
  elif signature == digital_bill._generate_metadata_hash():
136
136
  signature_valid = True
137
137
  verification_method = "digital_bill_metadata_hash"
138
- print(f"Verified: matches generated metadata_hash")
138
+ safe_print(f"Verified: matches generated metadata_hash")
139
139
 
140
140
  else:
141
- print(f"DigitalBill verification failed:")
142
- print(f" Calculated hash: {calculated_hash}")
143
- print(f" Signature: {signature}")
144
- print(f" Metadata hash: {metadata_hash}")
145
- print(f" Public key: {public_key}")
146
-
141
+ safe_print(f"DigitalBill verification failed:")
142
+ safe_print(f" Calculated hash: {calculated_hash}")
143
+ safe_print(f" Signature: {signature}")
144
+ safe_print(f" Metadata hash: {metadata_hash}")
145
+ safe_print(f" Public key: {public_key}")
147
146
  except Exception as e:
148
- print(f"DigitalBill verification error: {e}")
147
+ safe_print(f"DigitalBill verification error: {e}")
149
148
  import traceback
150
- print(f"Traceback: {traceback.format_exc()}")
149
+ safe_print(f"Traceback: {traceback.format_exc()}")
151
150
 
152
151
  # METHOD 4: Check simple concatenation hash
153
152
  elif signature_valid is None and signature:
@@ -156,7 +155,7 @@ class GTXGenesis:
156
155
  if signature == expected_simple_hash:
157
156
  signature_valid = True
158
157
  verification_method = "simple_hash"
159
- print(f"✅ Verified: hash(serial+denom+issued+timestamp)")
158
+ safe_print(f"✅ Verified: hash(serial+denom+issued+timestamp)")
160
159
 
161
160
  # METHOD 5: Check bill JSON hash
162
161
  elif signature_valid is None:
@@ -173,19 +172,19 @@ class GTXGenesis:
173
172
  if signature == bill_json_hash:
174
173
  signature_valid = True
175
174
  verification_method = "bill_json_hash"
176
- print(f"Verified: hash(bill_data_json)")
175
+ safe_print(f"Verified: hash(bill_data_json)")
177
176
 
178
177
  # Final fallback: accept any non-empty signature temporarily
179
178
  if signature_valid is None and signature and len(signature) > 10:
180
179
  signature_valid = True
181
180
  verification_method = "fallback_accept"
182
- print(f"Using fallback acceptance for signature")
181
+ safe_print(f"Using fallback acceptance for signature")
183
182
 
184
183
  # If all methods failed
185
184
  if signature_valid is None:
186
185
  signature_valid = False
187
186
  verification_method = "all_failed"
188
- print(f"All verification methods failed")
187
+ safe_print(f"All verification methods failed")
189
188
 
190
189
  # Return result in same format as endpoint
191
190
  if signature_valid:
@@ -337,7 +336,7 @@ class GTXGenesis:
337
336
  return computed_hash == bill_info['hash']
338
337
 
339
338
  except Exception as e:
340
- print(f"Bill verification error: {e}")
339
+ safe_print(f"Bill verification error: {e}")
341
340
  return False
342
341
 
343
342
  def _get_denomination_breakdown(self, bills: List[Dict]) -> Dict[int, int]:
@@ -0,0 +1 @@
1
+ from .miner import GenesisMiner
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,
@@ -289,7 +298,7 @@ class GenesisMiner:
289
298
  # Calculate merkleroot from transactions
290
299
  merkleroot = self._calculate_merkleroot(transactions)
291
300
 
292
- print(f"📊 Mining proof components:")
301
+ print(f" Mining proof components:")
293
302
  print(f" Block hash: {block_hash[:16]}...")
294
303
  print(f" Difficulty: {difficulty}")
295
304
  print(f" Nonce: {nonce}")
@@ -614,4 +623,94 @@ class GenesisMiner:
614
623
  return {
615
624
  "network_connected": False,
616
625
  "error": str(e)
617
- }
626
+ }
627
+
628
+ class Miner:
629
+ """
630
+ Miner class that uses lunalib directly to mine transfers, GTX genesis blocks, and rewards.
631
+ """
632
+
633
+ def __init__(self, config, data_manager, mining_started_callback=None, mining_completed_callback=None, block_mined_callback=None):
634
+ self.config = config
635
+ self.data_manager = data_manager
636
+ self.is_mining = False
637
+ self.blocks_mined = 0
638
+ self.total_reward = 0.0
639
+ self.mining_started_callback = mining_started_callback
640
+ self.mining_completed_callback = mining_completed_callback
641
+ self.block_mined_callback = block_mined_callback
642
+
643
+ self.mining_history = self.data_manager.load_mining_history()
644
+
645
+ # Use lunalib components
646
+ self.blockchain_manager = BlockchainManager(endpoint_url=config.node_url)
647
+ self.difficulty_system = DifficultySystem()
648
+ self.cuda_manager = CUDAManager()
649
+
650
+ self.current_hash = ""
651
+ self.current_nonce = 0
652
+ self.hash_rate = 0
653
+ self.mining_thread = None
654
+ self.should_stop_mining = False
655
+
656
+ def start_mining(self, mining_type: str):
657
+ """
658
+ Start mining based on the type: 'transfer', 'gtx_genesis', or 'reward'.
659
+ """
660
+ if self.is_mining:
661
+ return
662
+
663
+ self.is_mining = True
664
+ self.should_stop_mining = False
665
+
666
+ if self.mining_started_callback:
667
+ self.mining_started_callback()
668
+
669
+ if mining_type == 'transfer':
670
+ self.mine_transfers()
671
+ elif mining_type == 'gtx_genesis':
672
+ self.mine_genesis_blocks()
673
+ elif mining_type == 'reward':
674
+ self.mine_rewards()
675
+ else:
676
+ raise ValueError("Invalid mining type. Choose 'transfer', 'gtx_genesis', or 'reward'.")
677
+
678
+ def stop_mining(self):
679
+ """Stop the mining process."""
680
+ self.is_mining = False
681
+ self.should_stop_mining = True
682
+ if self.mining_thread and self.mining_thread.is_alive():
683
+ self.mining_thread.join()
684
+
685
+ def mine_transfers(self):
686
+ """Mine transfer transactions."""
687
+ while self.is_mining:
688
+ # Fetch transactions from the mempool
689
+ transactions = self.mempool_manager.get_pending_transactions()
690
+ if not transactions:
691
+ continue
692
+
693
+ # Mine a block with the transactions
694
+ block = self.blockchain_manager.create_block(transactions)
695
+ self.blockchain_manager.add_block(block)
696
+ self.mining_stats["transfers_mined"] += 1
697
+
698
+ def mine_genesis_blocks(self):
699
+ """Mine GTX genesis blocks."""
700
+ while self.is_mining:
701
+ # Create a genesis block
702
+ genesis_block = self.blockchain_manager.create_genesis_block()
703
+ self.blockchain_manager.add_block(genesis_block)
704
+ self.mining_stats["genesis_blocks_mined"] += 1
705
+
706
+ def mine_rewards(self):
707
+ """Mine rewards."""
708
+ while self.is_mining:
709
+ # Simulate mining rewards
710
+ reward = self.blockchain_manager.generate_reward()
711
+ self.blockchain_manager.add_reward(reward)
712
+ self.mining_stats["rewards_mined"] += 1
713
+
714
+ def get_mining_stats(self):
715
+ """Return the current mining statistics."""
716
+ return self.mining_stats
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: