lunalib 1.5.1__py3-none-any.whl → 1.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lunalib might be problematic. Click here for more details.

Files changed (48) hide show
  1. lunalib/core/__init__.py +14 -0
  2. lunalib/core/blockchain.py +183 -3
  3. lunalib/core/daemon.py +494 -0
  4. lunalib/core/p2p.py +361 -0
  5. lunalib/core/sm2.py +723 -723
  6. lunalib/core/wallet.py +714 -478
  7. lunalib/core/wallet_manager.py +638 -638
  8. lunalib/core/wallet_sync_helper.py +163 -163
  9. lunalib/gtx/digital_bill.py +10 -2
  10. lunalib/gtx/genesis.py +23 -24
  11. lunalib/mining/__init__.py +5 -0
  12. lunalib/mining/cuda_manager.py +23 -28
  13. lunalib/mining/difficulty.py +38 -0
  14. lunalib/mining/miner.py +526 -17
  15. lunalib/storage/cache.py +13 -4
  16. lunalib/storage/database.py +14 -5
  17. lunalib/storage/encryption.py +11 -2
  18. lunalib/transactions/security.py +19 -10
  19. lunalib/transactions/transactions.py +50 -41
  20. lunalib-1.7.2.dist-info/METADATA +27 -0
  21. lunalib-1.7.2.dist-info/RECORD +33 -0
  22. lunalib-1.7.2.dist-info/top_level.txt +1 -0
  23. core/__init__.py +0 -0
  24. core/blockchain.py +0 -172
  25. core/crypto.py +0 -32
  26. core/wallet.py +0 -408
  27. gtx/__init__.py +0 -0
  28. gtx/bill_registry.py +0 -122
  29. gtx/digital_bill.py +0 -273
  30. gtx/genesis.py +0 -338
  31. lunalib/requirements.txt +0 -44
  32. lunalib-1.5.1.dist-info/METADATA +0 -283
  33. lunalib-1.5.1.dist-info/RECORD +0 -53
  34. lunalib-1.5.1.dist-info/entry_points.txt +0 -2
  35. lunalib-1.5.1.dist-info/top_level.txt +0 -6
  36. mining/__init__.py +0 -0
  37. mining/cuda_manager.py +0 -137
  38. mining/difficulty.py +0 -106
  39. mining/miner.py +0 -107
  40. storage/__init__.py +0 -0
  41. storage/cache.py +0 -148
  42. storage/database.py +0 -222
  43. storage/encryption.py +0 -105
  44. transactions/__init__.py +0 -0
  45. transactions/security.py +0 -172
  46. transactions/transactions.py +0 -424
  47. transactions/validator.py +0 -71
  48. {lunalib-1.5.1.dist-info → lunalib-1.7.2.dist-info}/WHEEL +0 -0
lunalib/core/wallet.py CHANGED
@@ -13,12 +13,128 @@ from ..core.crypto import KeyManager
13
13
 
14
14
 
15
15
  class LunaWallet:
16
+ def lock_wallet(self, address=None):
17
+ """Lock the wallet (removes private key from memory)"""
18
+ addr = address or self.current_wallet_address
19
+ if not addr or addr not in self.wallets:
20
+ return {"success": False, "error": "Wallet not found"}
21
+ self.wallets[addr]["is_locked"] = True
22
+ if addr == self.current_wallet_address:
23
+ self.private_key = None
24
+ self.is_locked = True
25
+ return {"success": True}
26
+
27
+ def unlock_wallet(self, address, password):
28
+ """Unlock wallet with password. Returns dict with success/error."""
29
+ if address not in self.wallets:
30
+ return {"success": False, "error": f"Wallet {address} not found"}
31
+ wallet_data = self.wallets[address]
32
+ try:
33
+ if wallet_data.get("encrypted_private_key"):
34
+ key = base64.urlsafe_b64encode(
35
+ hashlib.sha256(password.encode()).digest()
36
+ )
37
+ fernet = Fernet(key)
38
+ decrypted_key = fernet.decrypt(wallet_data["encrypted_private_key"])
39
+ wallet_data["private_key"] = decrypted_key.decode()
40
+ wallet_data["is_locked"] = False
41
+ if self.current_wallet_address == address:
42
+ self.private_key = wallet_data["private_key"]
43
+ self.is_locked = False
44
+ if self._verify_wallet_integrity():
45
+ return {"success": True}
46
+ else:
47
+ return {
48
+ "success": False,
49
+ "error": "Cryptographic verification failed",
50
+ }
51
+ except Exception as e:
52
+ return {"success": False, "error": f"Unlock failed: {e}"}
53
+ return {"success": False, "error": "Unlock failed"}
54
+
55
+ def get_wallet_index(self):
56
+ """Return a list of all wallets with address, label, is_locked, balance, available_balance."""
57
+ index = []
58
+ for addr, data in self.wallets.items():
59
+ index.append(
60
+ {
61
+ "address": addr,
62
+ "label": data.get("label", ""),
63
+ "is_locked": data.get("is_locked", True),
64
+ "balance": data.get("balance", 0.0),
65
+ "available_balance": data.get("available_balance", 0.0),
66
+ }
67
+ )
68
+ return index
69
+
70
+ def get_wallet_info(self, address=None):
71
+ """Get info for a single wallet (address, label, balances, is_locked, created, public_key)."""
72
+ addr = address or self.current_wallet_address
73
+ if not addr or addr not in self.wallets:
74
+ return {"success": False, "error": "Wallet not found"}
75
+ data = self.wallets[addr]
76
+ return {
77
+ "address": addr,
78
+ "label": data.get("label", ""),
79
+ "is_locked": data.get("is_locked", True),
80
+ "balance": data.get("balance", 0.0),
81
+ "available_balance": data.get("available_balance", 0.0),
82
+ "created": data.get("created", 0),
83
+ "public_key": data.get("public_key", ""),
84
+ }
85
+
86
+ def get_wallet_balances(self, address=None):
87
+ """Get available and pending balances for a wallet."""
88
+ addr = address or self.current_wallet_address
89
+ if not addr or addr not in self.wallets:
90
+ return {"success": False, "error": "Wallet not found"}
91
+ data = self.wallets[addr]
92
+ pending_out = 0.0
93
+ pending_in = 0.0
94
+ if addr in self._pending_tx_cache:
95
+ pending_out, pending_in = self._compute_pending_totals(
96
+ self._pending_tx_cache[addr], addr
97
+ )
98
+ return {
99
+ "address": addr,
100
+ "balance": data.get("balance", 0.0),
101
+ "available_balance": data.get("available_balance", 0.0),
102
+ "pending_out": pending_out,
103
+ "pending_in": pending_in,
104
+ }
105
+
106
+ def get_wallet_transaction_history(self, address=None):
107
+ """Get confirmed and pending transactions for a wallet."""
108
+ addr = address or self.current_wallet_address
109
+ if not addr:
110
+ return {"success": False, "error": "No wallet selected"}
111
+ confirmed = self._confirmed_tx_cache.get(addr, [])
112
+ pending = self._pending_tx_cache.get(addr, [])
113
+ if not confirmed:
114
+ from lunalib.core.blockchain import BlockchainManager
115
+
116
+ blockchain = BlockchainManager()
117
+ confirmed = blockchain.scan_transactions_for_address(addr)
118
+ self._confirmed_tx_cache[addr] = confirmed
119
+ if not pending:
120
+ from lunalib.core.mempool import MempoolManager
121
+
122
+ mempool = MempoolManager()
123
+ pending = mempool.get_pending_transactions(addr, fetch_remote=True)
124
+ self._pending_tx_cache[addr] = pending
125
+ return {
126
+ "address": addr,
127
+ "confirmed": confirmed,
128
+ "pending": pending,
129
+ "total_confirmed": len(confirmed),
130
+ "total_pending": len(pending),
131
+ }
132
+
16
133
  def __init__(self, data_dir=None):
17
134
  self.data_dir = data_dir
18
135
  self.wallets = {}
19
136
  self.current_wallet_address = None
20
137
  self.key_manager = KeyManager()
21
-
22
138
  # Threading for asynchronous balance loading
23
139
  self.balance_thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=3)
24
140
  self.balance_callbacks = []
@@ -34,11 +150,10 @@ class LunaWallet:
34
150
  # ----------------------
35
151
  def _normalize_address(self, addr: str) -> str:
36
152
  if not addr:
37
- return ''
153
+ return ""
38
154
  addr_str = str(addr).strip("'\" ").lower()
39
- return addr_str[4:] if addr_str.startswith('lun_') else addr_str
40
-
41
-
155
+ return addr_str[4:] if addr_str.startswith("lun_") else addr_str
156
+
42
157
  def _reset_current_wallet(self):
43
158
  """Reset current wallet to empty state"""
44
159
  self.address = None
@@ -46,51 +161,51 @@ class LunaWallet:
46
161
  self.available_balance = 0.0 # Available balance (total - pending outgoing)
47
162
  self.created = time.time()
48
163
  self.private_key = None # Will store REAL SM2 private key
49
- self.public_key = None # Will store REAL SM2 public key
164
+ self.public_key = None # Will store REAL SM2 public key
50
165
  self.encrypted_private_key = None
51
166
  self.label = "New Wallet"
52
167
  self.is_locked = True
53
-
168
+
54
169
  # ============================================================================
55
170
  # REAL CRYPTOGRAPHIC KEY GENERATION (SM2 EAST ASIAN STANDARD)
56
171
  # ============================================================================
57
-
58
-
59
-
172
+
60
173
  def _sign_transaction_data(self, transaction_data):
61
174
  """Sign transaction data with SM2 private key"""
62
175
  if not self.private_key or self.is_locked:
63
176
  print("DEBUG: Cannot sign - wallet locked or no private key")
64
177
  return None
65
-
178
+
66
179
  # Convert transaction data to string for signing
67
180
  tx_string = json.dumps(transaction_data, sort_keys=True)
68
-
181
+
69
182
  # Sign with SM2
70
183
  signature = self.sm2.sign_message(self.private_key, tx_string)
71
-
184
+
72
185
  if signature:
73
- print(f"DEBUG: Transaction signed successfully, signature: {signature[:16]}...")
186
+ print(
187
+ f"DEBUG: Transaction signed successfully, signature: {signature[:16]}..."
188
+ )
74
189
  else:
75
190
  print("DEBUG: Failed to sign transaction")
76
-
191
+
77
192
  return signature
78
-
193
+
79
194
  def _verify_wallet_integrity(self) -> bool:
80
195
  """Basic cryptographic key verification"""
81
196
  # Check if keys exist
82
197
  if not self.private_key or not self.public_key:
83
198
  return False
84
-
199
+
85
200
  # Validate key formats
86
201
  if len(self.private_key) != 64: # 256-bit hex
87
202
  return False
88
-
89
- if not self.public_key.startswith('04') or len(self.public_key) != 130:
203
+
204
+ if not self.public_key.startswith("04") or len(self.public_key) != 130:
90
205
  return False
91
-
206
+
92
207
  return True
93
-
208
+
94
209
  def _generate_key_pair(self):
95
210
  """Generate REAL SM2 key pair"""
96
211
  return self.key_manager.generate_keypair()
@@ -98,181 +213,189 @@ class LunaWallet:
98
213
  def create_wallet(self, name, password):
99
214
  """Create a new wallet and set it as current"""
100
215
  print(f"DEBUG: Creating wallet '{name}'")
101
-
216
+
102
217
  # Generate REAL SM2 keys
103
218
  private_key, public_key, address = self._generate_key_pair()
104
-
219
+
105
220
  # Encrypt private key
106
221
  key = base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
107
222
  fernet = Fernet(key)
108
223
  encrypted_private_key = fernet.encrypt(private_key.encode())
109
-
224
+
110
225
  # Create wallet data
111
226
  wallet_data = {
112
- 'address': address,
113
- 'balance': 0.0,
114
- 'available_balance': 0.0,
115
- 'created': time.time(),
116
- 'private_key': private_key, # REAL SM2 private key
117
- 'public_key': public_key, # REAL SM2 public key
118
- 'encrypted_private_key': encrypted_private_key,
119
- 'label': name,
120
- 'is_locked': True,
121
- 'crypto_standard': 'SM2_GB/T_32918'
227
+ "address": address,
228
+ "balance": 0.0,
229
+ "available_balance": 0.0,
230
+ "created": time.time(),
231
+ "private_key": private_key, # REAL SM2 private key
232
+ "public_key": public_key, # REAL SM2 public key
233
+ "encrypted_private_key": encrypted_private_key,
234
+ "label": name,
235
+ "is_locked": True,
236
+ "crypto_standard": "SM2_GB/T_32918",
122
237
  }
123
-
238
+
124
239
  # Add to wallets collection
125
240
  self.wallets[address] = wallet_data
126
-
241
+
127
242
  # Set as current wallet
128
243
  self._set_current_wallet(wallet_data)
129
-
244
+
130
245
  print(f"DEBUG: Created and set current wallet {address}")
131
-
246
+
132
247
  return wallet_data
133
248
 
134
-
135
249
  def _generate_address(self, public_key_hex):
136
250
  """Generate address from SM2 public key"""
137
251
  return self.key_manager.derive_address(public_key_hex)
252
+
138
253
  def create_new_wallet(self, name, password):
139
254
  """
140
255
  Create a new wallet without switching to it
141
256
  Returns: wallet_data dict
142
257
  """
143
258
  print(f"DEBUG: Creating additional wallet '{name}'")
144
-
259
+
145
260
  # Generate REAL cryptographic keys
146
261
  private_key_hex, public_key_hex, address = self._generate_key_pair()
147
-
262
+
148
263
  # Encrypt private key
149
264
  key = base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
150
265
  fernet = Fernet(key)
151
266
  encrypted_private_key = fernet.encrypt(private_key_hex.encode())
152
-
267
+
153
268
  # Create wallet data
154
269
  new_wallet_data = {
155
- 'address': address,
156
- 'balance': 0.0,
157
- 'available_balance': 0.0,
158
- 'created': time.time(),
159
- 'private_key': private_key_hex,
160
- 'public_key': public_key_hex,
161
- 'encrypted_private_key': encrypted_private_key,
162
- 'label': name,
163
- 'is_locked': True,
164
- 'crypto_standard': 'SM2_GB/T_32918'
270
+ "address": address,
271
+ "balance": 0.0,
272
+ "available_balance": 0.0,
273
+ "created": time.time(),
274
+ "private_key": private_key_hex,
275
+ "public_key": public_key_hex,
276
+ "encrypted_private_key": encrypted_private_key,
277
+ "label": name,
278
+ "is_locked": True,
279
+ "crypto_standard": "SM2_GB/T_32918",
165
280
  }
166
-
281
+
167
282
  # Add to wallets collection
168
283
  self.wallets[address] = new_wallet_data
169
-
284
+
170
285
  print(f"DEBUG: Created wallet {address}, total wallets: {len(self.wallets)}")
171
-
286
+
172
287
  return new_wallet_data
173
-
288
+
174
289
  def _set_current_wallet(self, wallet_data):
175
290
  """Set the current wallet from wallet data"""
176
- self.current_wallet_address = wallet_data['address']
177
- self.address = wallet_data['address']
178
- self.balance = wallet_data['balance']
179
- self.available_balance = wallet_data['available_balance']
180
- self.created = wallet_data['created']
181
- self.private_key = wallet_data['private_key']
182
- self.public_key = wallet_data['public_key']
183
- self.encrypted_private_key = wallet_data['encrypted_private_key']
184
- self.label = wallet_data['label']
185
- self.is_locked = wallet_data.get('is_locked', True)
186
-
291
+ self.current_wallet_address = wallet_data["address"]
292
+ self.address = wallet_data["address"]
293
+ self.balance = wallet_data["balance"]
294
+ self.available_balance = wallet_data["available_balance"]
295
+ self.created = wallet_data["created"]
296
+ self.private_key = wallet_data["private_key"]
297
+ self.public_key = wallet_data["public_key"]
298
+ self.encrypted_private_key = wallet_data["encrypted_private_key"]
299
+ self.label = wallet_data["label"]
300
+ self.is_locked = wallet_data.get("is_locked", True)
301
+
187
302
  print(f"DEBUG: Set current wallet to {self.address}")
188
-
303
+
189
304
  def unlock_wallet(self, address, password):
190
305
  """Unlock wallet with password"""
191
306
  if address not in self.wallets:
192
307
  print(f"DEBUG: Wallet {address} not found in collection")
193
308
  return False
194
-
309
+
195
310
  wallet_data = self.wallets[address]
196
-
311
+
197
312
  try:
198
- if wallet_data.get('encrypted_private_key'):
313
+ if wallet_data.get("encrypted_private_key"):
199
314
  # Decrypt private key
200
- key = base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
315
+ key = base64.urlsafe_b64encode(
316
+ hashlib.sha256(password.encode()).digest()
317
+ )
201
318
  fernet = Fernet(key)
202
- decrypted_key = fernet.decrypt(wallet_data['encrypted_private_key'])
203
-
319
+ decrypted_key = fernet.decrypt(wallet_data["encrypted_private_key"])
320
+
204
321
  # Update wallet data
205
- wallet_data['private_key'] = decrypted_key.decode()
206
- wallet_data['is_locked'] = False
207
-
322
+ wallet_data["private_key"] = decrypted_key.decode()
323
+ wallet_data["is_locked"] = False
324
+
208
325
  # If this is the current wallet, update current state
209
326
  if self.current_wallet_address == address:
210
- self.private_key = wallet_data['private_key']
327
+ self.private_key = wallet_data["private_key"]
211
328
  self.is_locked = False
212
-
329
+
213
330
  print(f"DEBUG: Wallet {address} unlocked successfully")
214
331
  print(f"DEBUG: Private key available: {bool(self.private_key)}")
215
-
332
+
216
333
  # Verify cryptographic integrity after unlock
217
334
  if self._verify_wallet_integrity():
218
335
  print(f"DEBUG: Wallet cryptographic integrity verified")
219
336
  else:
220
- print(f"DEBUG: WARNING: Wallet unlocked but cryptographic verification failed")
221
-
337
+ print(
338
+ f"DEBUG: WARNING: Wallet unlocked but cryptographic verification failed"
339
+ )
340
+
222
341
  return True
223
342
  except Exception as e:
224
343
  print(f"DEBUG: Unlock failed: {e}")
225
-
344
+
226
345
  return False
227
-
346
+
228
347
  def switch_wallet(self, address, password=None):
229
348
  """Switch to a different wallet in the collection"""
230
349
  if address in self.wallets:
231
350
  wallet_data = self.wallets[address]
232
351
  self._set_current_wallet(wallet_data)
233
-
352
+
234
353
  # If password provided, unlock the wallet
235
354
  if password:
236
355
  return self.unlock_wallet(address, password)
237
-
356
+
238
357
  return True
239
-
358
+
240
359
  print(f"DEBUG: Cannot switch to {address} - not in wallet collection")
241
360
  return False
242
-
361
+
243
362
  # ============================================================================
244
363
  # BALANCE AND TRANSACTION METHODS
245
364
  # ============================================================================
246
-
365
+
247
366
  def calculate_available_balance(self) -> float:
248
367
  """Calculate available balance (total balance minus pending outgoing transactions)"""
249
368
  try:
250
369
  # Get total balance from blockchain
251
370
  total_balance = self._get_total_balance_from_blockchain()
252
-
371
+
253
372
  # Get pending outgoing amount
254
373
  pending_outgoing = self._get_pending_balance()
255
-
374
+
256
375
  # Also check for any incoming pending transactions
257
376
  incoming_pending = self._get_pending_incoming_balance()
258
-
377
+
259
378
  available_balance = max(0.0, total_balance - pending_outgoing)
260
-
379
+
261
380
  # Update both current wallet and wallets collection
262
381
  self.available_balance = available_balance
263
382
  self.balance = total_balance # Also update total balance
264
-
383
+
265
384
  if self.current_wallet_address in self.wallets:
266
- self.wallets[self.current_wallet_address]['available_balance'] = available_balance
267
- self.wallets[self.current_wallet_address]['balance'] = total_balance
268
-
269
- print(f"DEBUG: Balance calculated - Total: {total_balance}, Pending Out: {pending_outgoing}, Available: {available_balance}")
270
-
385
+ self.wallets[self.current_wallet_address][
386
+ "available_balance"
387
+ ] = available_balance
388
+ self.wallets[self.current_wallet_address]["balance"] = total_balance
389
+
390
+ print(
391
+ f"DEBUG: Balance calculated - Total: {total_balance}, Pending Out: {pending_outgoing}, Available: {available_balance}"
392
+ )
393
+
271
394
  if incoming_pending > 0:
272
395
  print(f"DEBUG: Also {incoming_pending} LUN incoming (pending)")
273
-
396
+
274
397
  return available_balance
275
-
398
+
276
399
  except Exception as e:
277
400
  print(f"DEBUG: Error calculating available balance: {e}")
278
401
  return self.balance # Fallback to total balance
@@ -281,37 +404,39 @@ class LunaWallet:
281
404
  """Compute confirmed balance from a list of transactions."""
282
405
  total_balance = 0.0
283
406
  for tx in transactions:
284
- tx_type = (tx.get('type') or '').lower()
285
- direction = tx.get('direction', '')
286
- amount = float(tx.get('amount', 0) or 0)
407
+ tx_type = (tx.get("type") or "").lower()
408
+ direction = tx.get("direction", "")
409
+ amount = float(tx.get("amount", 0) or 0)
287
410
 
288
- if tx_type == 'reward' or tx.get('from') == 'network':
411
+ if tx_type == "reward" or tx.get("from") == "network":
289
412
  total_balance += amount
290
- elif direction == 'incoming':
413
+ elif direction == "incoming":
291
414
  total_balance += amount
292
- elif direction == 'outgoing':
293
- fee = float(tx.get('fee', 0) or 0)
415
+ elif direction == "outgoing":
416
+ fee = float(tx.get("fee", 0) or 0)
294
417
  total_balance -= amount
295
418
  total_balance -= fee
296
419
 
297
420
  return max(0.0, total_balance)
298
421
 
299
- def _compute_pending_totals(self, pending_txs: List[Dict], address: str) -> Tuple[float, float]:
422
+ def _compute_pending_totals(
423
+ self, pending_txs: List[Dict], address: str
424
+ ) -> Tuple[float, float]:
300
425
  """Return (pending_outgoing, pending_incoming) for an address."""
301
426
  pending_out = 0.0
302
427
  pending_in = 0.0
303
428
 
304
429
  target_norm = self._normalize_address(address)
305
430
  for tx in pending_txs:
306
- from_norm = self._normalize_address(tx.get('from') or tx.get('sender'))
307
- to_norm = self._normalize_address(tx.get('to') or tx.get('receiver'))
431
+ from_norm = self._normalize_address(tx.get("from") or tx.get("sender"))
432
+ to_norm = self._normalize_address(tx.get("to") or tx.get("receiver"))
308
433
 
309
434
  if from_norm == target_norm:
310
- amount = float(tx.get('amount', 0) or 0)
311
- fee = float(tx.get('fee', 0) or tx.get('gas', 0) or 0)
435
+ amount = float(tx.get("amount", 0) or 0)
436
+ fee = float(tx.get("fee", 0) or tx.get("gas", 0) or 0)
312
437
  pending_out += amount + fee
313
438
  if to_norm == target_norm:
314
- amount = float(tx.get('amount', 0) or 0)
439
+ amount = float(tx.get("amount", 0) or 0)
315
440
  pending_in += amount
316
441
 
317
442
  return pending_out, pending_in
@@ -328,18 +453,18 @@ class LunaWallet:
328
453
  pending_out, pending_in = self._compute_pending_totals(pending, addr)
329
454
  available_balance = max(0.0, total_balance - pending_out)
330
455
 
331
- wallet_data['balance'] = total_balance
332
- wallet_data['available_balance'] = available_balance
456
+ wallet_data["balance"] = total_balance
457
+ wallet_data["available_balance"] = available_balance
333
458
 
334
459
  if addr == self.current_wallet_address:
335
460
  self.balance = total_balance
336
461
  self.available_balance = available_balance
337
462
 
338
463
  updated[addr] = {
339
- 'balance': total_balance,
340
- 'available_balance': available_balance,
341
- 'pending_out': pending_out,
342
- 'pending_in': pending_in
464
+ "balance": total_balance,
465
+ "available_balance": available_balance,
466
+ "pending_out": pending_out,
467
+ "pending_in": pending_in,
343
468
  }
344
469
 
345
470
  return updated
@@ -358,7 +483,9 @@ class LunaWallet:
358
483
  mempool = MempoolManager()
359
484
 
360
485
  confirmed_map = blockchain.scan_transactions_for_addresses(addresses)
361
- pending_map = mempool.get_pending_transactions_for_addresses(addresses, fetch_remote=True)
486
+ pending_map = mempool.get_pending_transactions_for_addresses(
487
+ addresses, fetch_remote=True
488
+ )
362
489
 
363
490
  # Reset caches with the latest snapshots
364
491
  self._confirmed_tx_cache = confirmed_map
@@ -374,7 +501,9 @@ class LunaWallet:
374
501
  """Convenience: scan blockchain once and mempool once, update all wallet balances."""
375
502
  return self.refresh_all_wallet_balances()
376
503
 
377
- def get_all_wallets_overview(self, include_transactions: bool = True) -> Dict[str, Dict]:
504
+ def get_all_wallets_overview(
505
+ self, include_transactions: bool = True
506
+ ) -> Dict[str, Dict]:
378
507
  """Return balances and (optionally) cached transactions for all wallets."""
379
508
  overview: Dict[str, Dict] = {}
380
509
  for addr, wallet_data in self.wallets.items():
@@ -382,19 +511,21 @@ class LunaWallet:
382
511
  pending = self._pending_tx_cache.get(addr, [])
383
512
 
384
513
  overview[addr] = {
385
- 'balance': wallet_data.get('balance', 0.0),
386
- 'available_balance': wallet_data.get('available_balance', 0.0),
387
- 'pending_out': self._compute_pending_totals(pending, addr)[0],
388
- 'pending_in': self._compute_pending_totals(pending, addr)[1],
514
+ "balance": wallet_data.get("balance", 0.0),
515
+ "available_balance": wallet_data.get("available_balance", 0.0),
516
+ "pending_out": self._compute_pending_totals(pending, addr)[0],
517
+ "pending_in": self._compute_pending_totals(pending, addr)[1],
389
518
  }
390
519
 
391
520
  if include_transactions:
392
- overview[addr]['confirmed_transactions'] = confirmed
393
- overview[addr]['pending_transactions'] = pending
521
+ overview[addr]["confirmed_transactions"] = confirmed
522
+ overview[addr]["pending_transactions"] = pending
394
523
 
395
524
  return overview
396
525
 
397
- def _apply_transaction_updates(self, confirmed_map: Dict[str, List[Dict]], pending_map: Dict[str, List[Dict]]):
526
+ def _apply_transaction_updates(
527
+ self, confirmed_map: Dict[str, List[Dict]], pending_map: Dict[str, List[Dict]]
528
+ ):
398
529
  """Update caches from monitor callbacks and recompute balances."""
399
530
  for addr, txs in confirmed_map.items():
400
531
  self._confirmed_tx_cache.setdefault(addr, []).extend(txs)
@@ -412,22 +543,25 @@ class LunaWallet:
412
543
  return None
413
544
 
414
545
  from lunalib.core.blockchain import BlockchainManager
546
+
415
547
  blockchain = BlockchainManager()
416
548
 
417
549
  # Run an initial refresh to seed caches and balances
418
550
  self.refresh_all_wallet_balances()
419
551
 
420
552
  def _on_update(payload: Dict):
421
- confirmed_map = payload.get('confirmed', {}) or {}
422
- pending_map = payload.get('pending', {}) or {}
553
+ confirmed_map = payload.get("confirmed", {}) or {}
554
+ pending_map = payload.get("pending", {}) or {}
423
555
  self._apply_transaction_updates(confirmed_map, pending_map)
424
556
 
425
- self._monitor_stop_event = blockchain.monitor_addresses(addresses, _on_update, poll_interval=poll_interval)
557
+ self._monitor_stop_event = blockchain.monitor_addresses(
558
+ addresses, _on_update, poll_interval=poll_interval
559
+ )
426
560
  return self._monitor_stop_event
427
561
 
428
562
  def stop_wallet_monitoring(self):
429
563
  """Stop the background monitor if running."""
430
- stop_event = getattr(self, '_monitor_stop_event', None)
564
+ stop_event = getattr(self, "_monitor_stop_event", None)
431
565
  if stop_event:
432
566
  stop_event.set()
433
567
 
@@ -436,320 +570,355 @@ class LunaWallet:
436
570
  self.sync_all_wallets_once()
437
571
  return self.start_wallet_monitoring(poll_interval=poll_interval)
438
572
 
439
- def get_all_balances_after_sync(self, include_transactions: bool = True) -> Dict[str, Dict]:
573
+ def get_all_balances_after_sync(
574
+ self, include_transactions: bool = True
575
+ ) -> Dict[str, Dict]:
440
576
  """One-shot sync then return per-wallet balances (and transactions if requested)."""
441
577
  self.sync_all_wallets_once()
442
578
  return self.get_all_wallets_overview(include_transactions=include_transactions)
579
+
443
580
  def _get_pending_balance(self) -> float:
444
581
  """Get total pending balance from mempool (outgoing pending transactions)"""
445
582
  try:
446
583
  from lunalib.core.mempool import MempoolManager
447
-
584
+
448
585
  mempool = MempoolManager()
449
-
586
+
450
587
  # Get pending transactions for this address
451
- pending_txs = mempool.get_pending_transactions(self.address, fetch_remote=True)
452
-
588
+ pending_txs = mempool.get_pending_transactions(
589
+ self.address, fetch_remote=True
590
+ )
591
+
453
592
  total_pending_outgoing = 0.0
454
-
593
+
455
594
  for tx in pending_txs:
456
595
  # Only count outgoing transactions (where we're the sender)
457
- if tx.get('from') == self.address:
458
- amount = float(tx.get('amount', 0))
459
- fee = float(tx.get('fee', 0) or tx.get('gas', 0) or 0)
596
+ if tx.get("from") == self.address:
597
+ amount = float(tx.get("amount", 0))
598
+ fee = float(tx.get("fee", 0) or tx.get("gas", 0) or 0)
460
599
  total_pending_outgoing += amount + fee
461
- print(f"🔍 Found pending outgoing: {amount} + {fee} fee = {amount + fee}")
462
-
600
+ print(
601
+ f"🔍 Found pending outgoing: {amount} + {fee} fee = {amount + fee}"
602
+ )
603
+
463
604
  return total_pending_outgoing
464
-
605
+
465
606
  except Exception as e:
466
607
  print(f"DEBUG: Error calculating pending balance: {e}")
467
608
  return 0.0
468
-
609
+
469
610
  def _get_pending_incoming_balance(self) -> float:
470
611
  """Get total pending incoming balance from mempool"""
471
612
  try:
472
613
  from lunalib.core.mempool import MempoolManager
473
-
614
+
474
615
  mempool = MempoolManager()
475
-
616
+
476
617
  # Get pending transactions for this address
477
- pending_txs = mempool.get_pending_transactions(self.address, fetch_remote=True)
478
-
618
+ pending_txs = mempool.get_pending_transactions(
619
+ self.address, fetch_remote=True
620
+ )
621
+
479
622
  total_pending_incoming = 0.0
480
-
623
+
481
624
  for tx in pending_txs:
482
625
  # Only count incoming transactions (where we're the receiver)
483
- if tx.get('to') == self.address:
484
- amount = float(tx.get('amount', 0))
626
+ if tx.get("to") == self.address:
627
+ amount = float(tx.get("amount", 0))
485
628
  total_pending_incoming += amount
486
629
  print(f"🔍 Found pending incoming: +{amount}")
487
-
630
+
488
631
  return total_pending_incoming
489
-
632
+
490
633
  except Exception as e:
491
634
  print(f"DEBUG: Error calculating pending incoming balance: {e}")
492
635
  return 0.0
493
-
636
+
494
637
  def _get_total_balance_from_blockchain(self) -> float:
495
638
  """Get total balance by scanning blockchain for confirmed transactions"""
496
639
  try:
497
640
  from lunalib.core.blockchain import BlockchainManager
498
-
641
+
499
642
  blockchain = BlockchainManager()
500
643
  transactions = blockchain.scan_transactions_for_address(self.address)
501
644
  total_balance = self._compute_confirmed_balance(transactions)
502
645
  return total_balance
503
-
646
+
504
647
  except Exception as e:
505
648
  print(f"DEBUG: Error getting blockchain balance: {e}")
506
649
  return self.balance
507
-
650
+
508
651
  def register_balance_callback(self, callback: Callable[[float, float], None]):
509
652
  """Register a callback to be called when balance is updated asynchronously"""
510
653
  self.balance_callbacks.append(callback)
511
-
654
+
512
655
  def start_async_balance_loading(self):
513
656
  """Start asynchronous balance loading in background thread"""
514
657
  if self.balance_loading:
515
658
  return # Already loading
516
-
659
+
517
660
  if time.time() - self.last_balance_update < self.balance_update_interval:
518
661
  return # Too soon since last update
519
-
662
+
520
663
  self.balance_loading = True
521
-
664
+
522
665
  def _async_balance_update():
523
666
  try:
524
667
  # Load balances asynchronously
525
668
  total_balance = self._get_total_balance_from_blockchain()
526
669
  available_balance = self.calculate_available_balance()
527
-
670
+
528
671
  # Update wallet state
529
672
  self.balance = total_balance
530
673
  self.available_balance = available_balance
531
674
  self.last_balance_update = time.time()
532
-
675
+
533
676
  # Update in wallets collection
534
677
  if self.current_wallet_address in self.wallets:
535
- self.wallets[self.current_wallet_address]['balance'] = total_balance
536
- self.wallets[self.current_wallet_address]['available_balance'] = available_balance
537
-
678
+ self.wallets[self.current_wallet_address]["balance"] = total_balance
679
+ self.wallets[self.current_wallet_address][
680
+ "available_balance"
681
+ ] = available_balance
682
+
538
683
  # Notify callbacks
539
684
  for callback in self.balance_callbacks:
540
685
  try:
541
686
  callback(total_balance, available_balance)
542
687
  except Exception as e:
543
688
  print(f"Error in balance callback: {e}")
544
-
545
- print(f"DEBUG: Async balance updated - Total: {total_balance}, Available: {available_balance}")
546
-
689
+
690
+ print(
691
+ f"DEBUG: Async balance updated - Total: {total_balance}, Available: {available_balance}"
692
+ )
693
+
547
694
  except Exception as e:
548
695
  print(f"DEBUG: Error in async balance loading: {e}")
549
696
  finally:
550
697
  self.balance_loading = False
551
-
698
+
552
699
  # Start in background thread
553
700
  self.balance_thread_pool.submit(_async_balance_update)
554
-
701
+
555
702
  def refresh_balance(self) -> bool:
556
703
  """Refresh both total and available balance from blockchain and mempool"""
557
704
  try:
558
705
  total_balance = self._get_total_balance_from_blockchain()
559
706
  available_balance = self.calculate_available_balance()
560
-
707
+
561
708
  # Update wallet state
562
709
  self.balance = total_balance
563
710
  self.available_balance = available_balance
564
-
711
+
565
712
  # Update in wallets collection
566
713
  if self.current_wallet_address in self.wallets:
567
- self.wallets[self.current_wallet_address]['balance'] = total_balance
568
- self.wallets[self.current_wallet_address]['available_balance'] = available_balance
569
-
570
- print(f"DEBUG: Balance refreshed - Total: {total_balance}, Available: {available_balance}")
714
+ self.wallets[self.current_wallet_address]["balance"] = total_balance
715
+ self.wallets[self.current_wallet_address][
716
+ "available_balance"
717
+ ] = available_balance
718
+
719
+ print(
720
+ f"DEBUG: Balance refreshed - Total: {total_balance}, Available: {available_balance}"
721
+ )
571
722
  return True
572
-
723
+
573
724
  except Exception as e:
574
725
  print(f"DEBUG: Error refreshing balance: {e}")
575
726
  return False
576
-
577
- def send_transaction(self, to_address: str, amount: float, memo: str = "", password: str = None) -> bool:
727
+
728
+ def send_transaction(
729
+ self, to_address: str, amount: float, memo: str = "", password: str = None
730
+ ) -> bool:
578
731
  """Send transaction using REAL SM2 signatures"""
579
732
  try:
580
733
  print(f"[SM2] Sending {amount} to {to_address}")
581
-
734
+
582
735
  # 1. Basic validation
583
736
  if self.is_locked or not self.private_key:
584
737
  print("[SM2] Wallet locked or no private key")
585
738
  return False
586
-
739
+
587
740
  # 2. Check key integrity
588
741
  if not self._verify_wallet_integrity():
589
742
  print("[SM2] Invalid cryptographic keys")
590
743
  return False
591
-
744
+
592
745
  # 3. Balance check
593
746
  self.refresh_balance()
594
747
  if amount > self.get_available_balance():
595
- print(f"[SM2] Insufficient balance: {self.get_available_balance()} < {amount}")
748
+ print(
749
+ f"[SM2] Insufficient balance: {self.get_available_balance()} < {amount}"
750
+ )
596
751
  return False
597
-
752
+
598
753
  # 4. Create and sign transaction
599
754
  from lunalib.transactions.transactions import TransactionManager
755
+
600
756
  tx_manager = TransactionManager()
601
-
757
+
602
758
  transaction = tx_manager.create_transaction(
603
759
  from_address=self.address,
604
760
  to_address=to_address,
605
761
  amount=amount,
606
762
  private_key=self.private_key,
607
763
  memo=memo,
608
- transaction_type="transfer"
764
+ transaction_type="transfer",
765
+ )
766
+
767
+ print(
768
+ f"[SM2] Transaction created: {transaction.get('hash', 'no_hash')[:16]}"
609
769
  )
610
-
611
- print(f"[SM2] Transaction created: {transaction.get('hash', 'no_hash')[:16]}")
612
-
770
+
613
771
  # 5. Validate
614
772
  is_valid, message = tx_manager.validate_transaction(transaction)
615
773
  if not is_valid:
616
774
  print(f"[SM2] Validation failed: {message}")
617
775
  return False
618
-
776
+
619
777
  # 6. Broadcast
620
778
  success, message = tx_manager.send_transaction(transaction)
621
779
  if success:
622
780
  print(f"[SM2] Transaction broadcast: {message}")
623
-
781
+
624
782
  # Update balance immediately
625
- total_cost = amount + transaction.get('fee', 0)
783
+ total_cost = amount + transaction.get("fee", 0)
626
784
  self.available_balance -= total_cost
627
785
  if self.current_wallet_address in self.wallets:
628
- self.wallets[self.current_wallet_address]['available_balance'] = self.available_balance
629
-
786
+ self.wallets[self.current_wallet_address][
787
+ "available_balance"
788
+ ] = self.available_balance
789
+
630
790
  # Save state
631
791
  self.save_wallet_data()
632
-
792
+
633
793
  # Trigger async balance refresh to get accurate data
634
794
  self.start_async_balance_loading()
635
-
795
+
636
796
  return True
637
797
  else:
638
798
  print(f"[SM2] Broadcast failed: {message}")
639
799
  return False
640
-
800
+
641
801
  except Exception as e:
642
802
  print(f"[SM2] Error: {e}")
643
803
  return False
644
-
804
+
645
805
  def save_wallet_data(self):
646
806
  """Save wallet data to file"""
647
807
  try:
648
808
  print(f"[WALLET] Saving wallet data...")
649
-
809
+
650
810
  # Prepare wallet data
651
811
  wallet_data = {
652
- 'version': '2.0',
653
- 'address': self.address,
654
- 'public_key': self.public_key,
655
- 'private_key': self.private_key, # WARNING: In production, encrypt this!
656
- 'balance': self.balance,
657
- 'available_balance': self.available_balance,
658
- 'transactions': self.transactions,
659
- 'created_at': self.created_at,
660
- 'last_sync': time.time(),
661
- 'network': self.network,
662
- 'key_type': 'SM2' # Indicate SM2 key type
812
+ "version": "2.0",
813
+ "address": self.address,
814
+ "public_key": self.public_key,
815
+ "private_key": self.private_key, # WARNING: In production, encrypt this!
816
+ "balance": self.balance,
817
+ "available_balance": self.available_balance,
818
+ "transactions": self.transactions,
819
+ "created_at": self.created_at,
820
+ "last_sync": time.time(),
821
+ "network": self.network,
822
+ "key_type": "SM2", # Indicate SM2 key type
663
823
  }
664
-
824
+
665
825
  # Determine save path
666
- if hasattr(self, 'wallet_file'):
826
+ if hasattr(self, "wallet_file"):
667
827
  save_path = self.wallet_file
668
828
  else:
669
829
  # Default save location
670
830
  import os
671
- wallet_dir = os.path.expanduser('~/.lunawallet')
831
+
832
+ wallet_dir = os.path.expanduser("~/.lunawallet")
672
833
  os.makedirs(wallet_dir, exist_ok=True)
673
- save_path = os.path.join(wallet_dir, f'{self.address}.json')
674
-
834
+ save_path = os.path.join(wallet_dir, f"{self.address}.json")
835
+
675
836
  # Save to file
676
837
  import json
677
- with open(save_path, 'w') as f:
838
+
839
+ with open(save_path, "w") as f:
678
840
  json.dump(wallet_data, f, indent=2)
679
-
841
+
680
842
  print(f"[WALLET] Wallet data saved to {save_path}")
681
843
  return True
682
-
844
+
683
845
  except Exception as e:
684
846
  print(f"[WALLET ERROR] Failed to save wallet data: {e}")
685
847
  import traceback
848
+
686
849
  traceback.print_exc()
687
850
  return False
688
-
851
+
689
852
  def load_wallet_data(self, wallet_file=None):
690
853
  """Load wallet data from file"""
691
854
  try:
692
855
  print(f"[WALLET] Loading wallet data...")
693
-
856
+
694
857
  if wallet_file:
695
858
  self.wallet_file = wallet_file
696
859
  load_path = wallet_file
697
- elif hasattr(self, 'wallet_file'):
860
+ elif hasattr(self, "wallet_file"):
698
861
  load_path = self.wallet_file
699
862
  else:
700
863
  # Try to find wallet file
701
864
  import os
702
- wallet_dir = os.path.expanduser('~/.lunawallet')
865
+
866
+ wallet_dir = os.path.expanduser("~/.lunawallet")
703
867
  # Look for any .json file
704
- wallet_files = [f for f in os.listdir(wallet_dir) if f.endswith('.json')]
868
+ wallet_files = [
869
+ f for f in os.listdir(wallet_dir) if f.endswith(".json")
870
+ ]
705
871
  if not wallet_files:
706
872
  print("[WALLET] No wallet file found")
707
873
  return False
708
874
  load_path = os.path.join(wallet_dir, wallet_files[0])
709
875
  self.wallet_file = load_path
710
-
876
+
711
877
  # Load from file
712
878
  import json
713
- with open(load_path, 'r') as f:
879
+
880
+ with open(load_path, "r") as f:
714
881
  wallet_data = json.load(f)
715
-
882
+
716
883
  # Restore wallet data
717
- self.address = wallet_data.get('address', '')
718
- self.public_key = wallet_data.get('public_key', '')
719
- self.private_key = wallet_data.get('private_key', '')
720
- self.balance = wallet_data.get('balance', 0.0)
721
- self.available_balance = wallet_data.get('available_balance', 0.0)
722
- self.transactions = wallet_data.get('transactions', [])
723
- self.created_at = wallet_data.get('created_at', time.time())
724
- self.network = wallet_data.get('network', 'mainnet')
725
-
884
+ self.address = wallet_data.get("address", "")
885
+ self.public_key = wallet_data.get("public_key", "")
886
+ self.private_key = wallet_data.get("private_key", "")
887
+ self.balance = wallet_data.get("balance", 0.0)
888
+ self.available_balance = wallet_data.get("available_balance", 0.0)
889
+ self.transactions = wallet_data.get("transactions", [])
890
+ self.created_at = wallet_data.get("created_at", time.time())
891
+ self.network = wallet_data.get("network", "mainnet")
892
+
726
893
  print(f"[WALLET] Wallet loaded: {self.address}")
727
894
  print(f"[WALLET] Balance: {self.balance}")
728
-
895
+
729
896
  # Initialize SM2 if we have keys
730
897
  if self.public_key and self.private_key:
731
898
  self._initialize_sm2()
732
-
899
+
733
900
  # Start async balance loading
734
901
  self.start_async_balance_loading()
735
-
902
+
736
903
  return True
737
-
904
+
738
905
  except Exception as e:
739
906
  print(f"[WALLET ERROR] Failed to load wallet data: {e}")
740
907
  return False
741
-
908
+
742
909
  def _initialize_sm2(self):
743
910
  """Initialize SM2 crypto if keys exist"""
744
911
  try:
745
912
  if self.public_key and self.private_key:
746
913
  from ..core.crypto import KeyManager
914
+
747
915
  self.key_manager = KeyManager()
748
916
  print(f"[WALLET] SM2 initialized with existing keys")
749
917
  return True
750
918
  except Exception as e:
751
919
  print(f"[WALLET ERROR] Failed to initialize SM2: {e}")
752
920
  return False
921
+
753
922
  def get_transaction_history(self) -> dict:
754
923
  """Get complete transaction history (both pending and confirmed).
755
924
  Uses cached transactions if available, otherwise scans blockchain/mempool.
@@ -759,49 +928,62 @@ class LunaWallet:
759
928
  # Try to use cache first (from monitoring or sync)
760
929
  confirmed_txs = self._confirmed_tx_cache.get(self.address, [])
761
930
  pending_txs = self._pending_tx_cache.get(self.address, [])
762
-
931
+
763
932
  # If cache is empty, perform fresh scan
764
933
  if not confirmed_txs:
765
934
  from lunalib.core.blockchain import BlockchainManager
766
935
  from lunalib.core.mempool import MempoolManager
767
-
936
+
768
937
  blockchain = BlockchainManager()
769
938
  mempool = MempoolManager()
770
-
939
+
771
940
  # Get confirmed transactions from blockchain (includes mining rewards)
772
941
  confirmed_txs = blockchain.scan_transactions_for_address(self.address)
773
942
  self._confirmed_tx_cache[self.address] = confirmed_txs
774
-
943
+
775
944
  # Get pending transactions from mempool
776
- pending_txs = mempool.get_pending_transactions(self.address, fetch_remote=True)
945
+ pending_txs = mempool.get_pending_transactions(
946
+ self.address, fetch_remote=True
947
+ )
777
948
  self._pending_tx_cache[self.address] = pending_txs
778
-
949
+
779
950
  # Count by type for debugging
780
- reward_count = sum(1 for tx in confirmed_txs
781
- if tx.get('type', '').lower() in ['reward', 'mining']
782
- or tx.get('from') == 'network')
783
-
951
+ reward_count = sum(
952
+ 1
953
+ for tx in confirmed_txs
954
+ if tx.get("type", "").lower() in ["reward", "mining"]
955
+ or tx.get("from") == "network"
956
+ )
957
+
784
958
  return {
785
- 'confirmed': confirmed_txs,
786
- 'pending': pending_txs,
787
- 'total_confirmed': len(confirmed_txs),
788
- 'total_pending': len(pending_txs),
789
- 'reward_count': reward_count
959
+ "confirmed": confirmed_txs,
960
+ "pending": pending_txs,
961
+ "total_confirmed": len(confirmed_txs),
962
+ "total_pending": len(pending_txs),
963
+ "reward_count": reward_count,
790
964
  }
791
965
  except Exception as e:
792
966
  print(f"DEBUG: Error getting transaction history: {e}")
793
- return {'confirmed': [], 'pending': [], 'total_confirmed': 0, 'total_pending': 0, 'reward_count': 0}
794
-
795
- def get_wallet_transactions(self, address: str = None, include_pending: bool = True) -> Dict[str, List[Dict]]:
967
+ return {
968
+ "confirmed": [],
969
+ "pending": [],
970
+ "total_confirmed": 0,
971
+ "total_pending": 0,
972
+ "reward_count": 0,
973
+ }
974
+
975
+ def get_wallet_transactions(
976
+ self, address: str = None, include_pending: bool = True
977
+ ) -> Dict[str, List[Dict]]:
796
978
  """Get ALL transactions for a wallet including mining rewards, transfers, and pending.
797
-
979
+
798
980
  This is the comprehensive transaction getter that ensures ALL reward transactions
799
981
  (mining rewards, explicit reward transactions) are included.
800
-
982
+
801
983
  Args:
802
984
  address: wallet address to query (defaults to current wallet address)
803
985
  include_pending: whether to include pending mempool transactions (default: True)
804
-
986
+
805
987
  Returns:
806
988
  Dict with:
807
989
  - 'confirmed': list of all confirmed transactions (transfers + mining rewards)
@@ -813,70 +995,86 @@ class LunaWallet:
813
995
  """
814
996
  if address is None:
815
997
  address = self.address
816
-
998
+
817
999
  if not address:
818
1000
  return {
819
- 'confirmed': [],
820
- 'pending': [],
821
- 'reward_transactions': [],
822
- 'transfer_transactions': [],
823
- 'incoming_transfers': [],
824
- 'outgoing_transfers': [],
825
- 'total_rewards': 0,
826
- 'total_transfers': 0,
827
- 'total_incoming': 0,
828
- 'total_outgoing': 0
1001
+ "confirmed": [],
1002
+ "pending": [],
1003
+ "reward_transactions": [],
1004
+ "transfer_transactions": [],
1005
+ "incoming_transfers": [],
1006
+ "outgoing_transfers": [],
1007
+ "total_rewards": 0,
1008
+ "total_transfers": 0,
1009
+ "total_incoming": 0,
1010
+ "total_outgoing": 0,
829
1011
  }
830
-
1012
+
831
1013
  try:
832
1014
  # Normalize address for comparison
833
1015
  norm_addr = self._normalize_address(address)
834
-
1016
+
835
1017
  # Use cache if available
836
1018
  confirmed_txs = self._confirmed_tx_cache.get(norm_addr, [])
837
- pending_txs = self._pending_tx_cache.get(norm_addr, []) if include_pending else []
838
-
1019
+ pending_txs = (
1020
+ self._pending_tx_cache.get(norm_addr, []) if include_pending else []
1021
+ )
1022
+
839
1023
  # If cache is empty, perform fresh scan
840
1024
  if not confirmed_txs:
841
1025
  from lunalib.core.blockchain import BlockchainManager
1026
+
842
1027
  blockchain = BlockchainManager()
843
1028
  confirmed_txs = blockchain.scan_transactions_for_address(address)
844
1029
  self._confirmed_tx_cache[norm_addr] = confirmed_txs
845
-
1030
+
846
1031
  if include_pending and not pending_txs:
847
1032
  from lunalib.core.mempool import MempoolManager
1033
+
848
1034
  mempool = MempoolManager()
849
- pending_txs = mempool.get_pending_transactions(address, fetch_remote=True)
1035
+ pending_txs = mempool.get_pending_transactions(
1036
+ address, fetch_remote=True
1037
+ )
850
1038
  self._pending_tx_cache[norm_addr] = pending_txs
851
-
1039
+
852
1040
  # Separate rewards and transfers based on type and source
853
1041
  # Rewards: explicitly marked as reward/mining type, or from network
854
- reward_txs = [tx for tx in confirmed_txs
855
- if tx.get('type', '').lower() in ['reward', 'mining', 'gtx_genesis']
856
- or tx.get('from') == 'network']
857
-
1042
+ reward_txs = [
1043
+ tx
1044
+ for tx in confirmed_txs
1045
+ if tx.get("type", "").lower() in ["reward", "mining", "gtx_genesis"]
1046
+ or tx.get("from") == "network"
1047
+ ]
1048
+
858
1049
  # Transfers: anything that's NOT a reward (includes both incoming and outgoing)
859
- transfer_txs = [tx for tx in confirmed_txs
860
- if tx.get('type', '').lower() not in ['reward', 'mining', 'gtx_genesis']
861
- and tx.get('from') != 'network']
862
-
1050
+ transfer_txs = [
1051
+ tx
1052
+ for tx in confirmed_txs
1053
+ if tx.get("type", "").lower() not in ["reward", "mining", "gtx_genesis"]
1054
+ and tx.get("from") != "network"
1055
+ ]
1056
+
863
1057
  # Separate incoming vs outgoing transfers
864
- incoming_transfers = [tx for tx in transfer_txs if tx.get('direction') == 'incoming']
865
- outgoing_transfers = [tx for tx in transfer_txs if tx.get('direction') == 'outgoing']
866
-
1058
+ incoming_transfers = [
1059
+ tx for tx in transfer_txs if tx.get("direction") == "incoming"
1060
+ ]
1061
+ outgoing_transfers = [
1062
+ tx for tx in transfer_txs if tx.get("direction") == "outgoing"
1063
+ ]
1064
+
867
1065
  result = {
868
- 'confirmed': confirmed_txs,
869
- 'pending': pending_txs,
870
- 'reward_transactions': reward_txs,
871
- 'transfer_transactions': transfer_txs,
872
- 'incoming_transfers': incoming_transfers,
873
- 'outgoing_transfers': outgoing_transfers,
874
- 'total_rewards': len(reward_txs),
875
- 'total_transfers': len(transfer_txs),
876
- 'total_incoming': len(incoming_transfers),
877
- 'total_outgoing': len(outgoing_transfers)
1066
+ "confirmed": confirmed_txs,
1067
+ "pending": pending_txs,
1068
+ "reward_transactions": reward_txs,
1069
+ "transfer_transactions": transfer_txs,
1070
+ "incoming_transfers": incoming_transfers,
1071
+ "outgoing_transfers": outgoing_transfers,
1072
+ "total_rewards": len(reward_txs),
1073
+ "total_transfers": len(transfer_txs),
1074
+ "total_incoming": len(incoming_transfers),
1075
+ "total_outgoing": len(outgoing_transfers),
878
1076
  }
879
-
1077
+
880
1078
  print(f"DEBUG: get_wallet_transactions({address}):")
881
1079
  print(f" - Mining Rewards: {len(reward_txs)}")
882
1080
  print(f" - Incoming Transfers: {len(incoming_transfers)}")
@@ -884,194 +1082,204 @@ class LunaWallet:
884
1082
  print(f" - Total Transfers: {len(transfer_txs)}")
885
1083
  print(f" - Pending: {len(pending_txs)}")
886
1084
  print(f" - Total Confirmed: {len(confirmed_txs)}")
887
-
1085
+
888
1086
  return result
889
-
1087
+
890
1088
  except Exception as e:
891
1089
  print(f"DEBUG: Error getting wallet transactions: {e}")
892
1090
  import traceback
1091
+
893
1092
  traceback.print_exc()
894
1093
  return {
895
- 'confirmed': [],
896
- 'pending': [],
897
- 'reward_transactions': [],
898
- 'transfer_transactions': [],
899
- 'incoming_transfers': [],
900
- 'outgoing_transfers': [],
901
- 'total_rewards': 0,
902
- 'total_transfers': 0,
903
- 'total_incoming': 0,
904
- 'total_outgoing': 0
1094
+ "confirmed": [],
1095
+ "pending": [],
1096
+ "reward_transactions": [],
1097
+ "transfer_transactions": [],
1098
+ "incoming_transfers": [],
1099
+ "outgoing_transfers": [],
1100
+ "total_rewards": 0,
1101
+ "total_transfers": 0,
1102
+ "total_incoming": 0,
1103
+ "total_outgoing": 0,
905
1104
  }
906
-
1105
+
907
1106
  # ============================================================================
908
1107
  # WALLET INFO AND UTILITIES
909
1108
  # ============================================================================
910
-
1109
+
911
1110
  @property
912
1111
  def is_unlocked(self):
913
1112
  """Check if current wallet is unlocked"""
914
1113
  if not self.current_wallet_address:
915
1114
  return False
916
1115
  wallet_data = self.wallets.get(self.current_wallet_address, {})
917
- return not wallet_data.get('is_locked', True)
918
-
1116
+ return not wallet_data.get("is_locked", True)
1117
+
919
1118
  def export_private_key(self, address, password):
920
1119
  """Export private key with password decryption"""
921
1120
  if address not in self.wallets:
922
1121
  return None
923
-
1122
+
924
1123
  wallet_data = self.wallets[address]
925
-
1124
+
926
1125
  try:
927
- if wallet_data.get('encrypted_private_key'):
928
- key = base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
1126
+ if wallet_data.get("encrypted_private_key"):
1127
+ key = base64.urlsafe_b64encode(
1128
+ hashlib.sha256(password.encode()).digest()
1129
+ )
929
1130
  fernet = Fernet(key)
930
- decrypted_key = fernet.decrypt(wallet_data['encrypted_private_key'])
1131
+ decrypted_key = fernet.decrypt(wallet_data["encrypted_private_key"])
931
1132
  return decrypted_key.decode()
932
1133
  except:
933
1134
  pass
934
1135
  return None
935
-
1136
+
936
1137
  def import_wallet(self, wallet_data, password=None):
937
1138
  """Import wallet from data"""
938
1139
  if isinstance(wallet_data, dict):
939
- address = wallet_data.get('address')
1140
+ address = wallet_data.get("address")
940
1141
  if not address:
941
1142
  return False
942
-
1143
+
943
1144
  # Check if wallet uses SM2 cryptography
944
- if wallet_data.get('crypto_standard') != 'SM2_GB/T_32918':
945
- print(f"DEBUG: WARNING: Importing wallet without SM2 cryptography standard")
946
-
1145
+ if wallet_data.get("crypto_standard") != "SM2_GB/T_32918":
1146
+ print(
1147
+ f"DEBUG: WARNING: Importing wallet without SM2 cryptography standard"
1148
+ )
1149
+
947
1150
  # Add to wallets collection
948
1151
  self.wallets[address] = wallet_data.copy()
949
-
1152
+
950
1153
  # Set as current wallet
951
1154
  self._set_current_wallet(wallet_data)
952
-
953
- if password and wallet_data.get('encrypted_private_key'):
1155
+
1156
+ if password and wallet_data.get("encrypted_private_key"):
954
1157
  return self.unlock_wallet(address, password)
955
-
1158
+
956
1159
  return True
957
1160
  return False
958
-
1161
+
959
1162
  def update_balance(self, new_balance):
960
1163
  """Update current wallet balance"""
961
1164
  self.balance = float(new_balance)
962
1165
  self.available_balance = float(new_balance)
963
-
1166
+
964
1167
  if self.current_wallet_address and self.current_wallet_address in self.wallets:
965
- self.wallets[self.current_wallet_address]['balance'] = self.balance
966
- self.wallets[self.current_wallet_address]['available_balance'] = self.available_balance
967
-
1168
+ self.wallets[self.current_wallet_address]["balance"] = self.balance
1169
+ self.wallets[self.current_wallet_address][
1170
+ "available_balance"
1171
+ ] = self.available_balance
1172
+
968
1173
  return True
969
-
1174
+
970
1175
  def get_balance(self):
971
1176
  """Get current wallet total balance"""
972
1177
  return self.balance
973
-
1178
+
974
1179
  def get_available_balance(self):
975
1180
  """Get current wallet available balance"""
976
1181
  return self.available_balance
977
-
1182
+
978
1183
  def get_wallet_by_address(self, address):
979
1184
  """Get wallet by address from wallets collection"""
980
1185
  return self.wallets.get(address)
981
-
1186
+
982
1187
  def list_wallets(self):
983
1188
  """List all wallets in collection"""
984
1189
  return list(self.wallets.keys())
985
-
1190
+
986
1191
  def get_current_wallet_info(self):
987
1192
  """Get current wallet information"""
988
1193
  if not self.current_wallet_address:
989
1194
  return None
990
-
1195
+
991
1196
  return self.wallets.get(self.current_wallet_address)
992
-
1197
+
993
1198
  def get_wallet_info(self):
994
1199
  """Get complete wallet information for current wallet"""
995
1200
  if not self.address:
996
1201
  return None
997
-
1202
+
998
1203
  self.refresh_balance()
999
-
1204
+
1000
1205
  return {
1001
- 'address': self.address,
1002
- 'balance': self.balance,
1003
- 'available_balance': self.available_balance,
1004
- 'created': self.created,
1005
- 'private_key': self.private_key,
1006
- 'public_key': self.public_key,
1007
- 'encrypted_private_key': self.encrypted_private_key,
1008
- 'label': self.label,
1009
- 'is_locked': self.is_locked,
1010
- 'crypto_standard': 'SM2_GB/T_32918'
1206
+ "address": self.address,
1207
+ "balance": self.balance,
1208
+ "available_balance": self.available_balance,
1209
+ "created": self.created,
1210
+ "private_key": self.private_key,
1211
+ "public_key": self.public_key,
1212
+ "encrypted_private_key": self.encrypted_private_key,
1213
+ "label": self.label,
1214
+ "is_locked": self.is_locked,
1215
+ "crypto_standard": "SM2_GB/T_32918",
1011
1216
  }
1012
-
1217
+
1013
1218
  # ============================================================================
1014
1219
  # UNIFIED WALLET STATE MANAGER INTEGRATION
1015
1220
  # ============================================================================
1016
-
1221
+
1017
1222
  def sync_with_state_manager(self, blockchain=None, mempool=None) -> Dict:
1018
1223
  """
1019
1224
  Sync all registered wallets with the unified WalletStateManager.
1020
1225
  Scans blockchain once and merges mempool data for all wallets.
1021
-
1226
+
1022
1227
  Parameters:
1023
1228
  blockchain: BlockchainManager instance (required)
1024
1229
  mempool: MempoolManager instance (required)
1025
-
1230
+
1026
1231
  Returns: Dictionary of wallet summaries with balances and transactions
1027
1232
  """
1028
1233
  try:
1029
1234
  if not blockchain or not mempool:
1030
1235
  print("❌ blockchain and mempool instances required")
1031
1236
  return {}
1032
-
1237
+
1033
1238
  from .wallet_manager import get_wallet_manager
1034
-
1239
+
1035
1240
  state_manager = get_wallet_manager()
1036
-
1241
+
1037
1242
  # Register all wallets with state manager if not already done
1038
1243
  addresses = list(self.wallets.keys())
1039
1244
  state_manager.register_wallets(addresses)
1040
-
1245
+
1041
1246
  print(f"🔄 Syncing {len(addresses)} wallets...")
1042
-
1247
+
1043
1248
  # Get data from blockchain and mempool (single scan)
1044
1249
  blockchain_txs = blockchain.scan_transactions_for_addresses(addresses)
1045
1250
  mempool_txs = mempool.get_pending_transactions_for_addresses(addresses)
1046
-
1251
+
1047
1252
  # Sync state manager with the data
1048
1253
  state_manager.sync_wallets_from_sources(blockchain_txs, mempool_txs)
1049
-
1254
+
1050
1255
  # Update LunaWallet balances from state manager
1051
1256
  balances = state_manager.get_all_balances()
1052
1257
  for address, balance_data in balances.items():
1053
1258
  if address in self.wallets:
1054
- self.wallets[address]['balance'] = balance_data['confirmed_balance']
1055
- self.wallets[address]['available_balance'] = balance_data['available_balance']
1056
-
1259
+ self.wallets[address]["balance"] = balance_data["confirmed_balance"]
1260
+ self.wallets[address]["available_balance"] = balance_data[
1261
+ "available_balance"
1262
+ ]
1263
+
1057
1264
  # Update current wallet
1058
1265
  if self.current_wallet_address and self.current_wallet_address in balances:
1059
1266
  balance_data = balances[self.current_wallet_address]
1060
- self.balance = balance_data['confirmed_balance']
1061
- self.available_balance = balance_data['available_balance']
1062
-
1267
+ self.balance = balance_data["confirmed_balance"]
1268
+ self.available_balance = balance_data["available_balance"]
1269
+
1063
1270
  # Return summaries
1064
1271
  summaries = state_manager.get_all_summaries()
1065
1272
  print(f"✅ Sync complete - {len(summaries)} wallets updated")
1066
-
1273
+
1067
1274
  return summaries
1068
-
1275
+
1069
1276
  except Exception as e:
1070
1277
  print(f"❌ Sync error: {e}")
1071
1278
  import traceback
1279
+
1072
1280
  traceback.print_exc()
1073
1281
  return {}
1074
-
1282
+
1075
1283
  def get_wallet_details(self, address: str = None) -> Optional[Dict]:
1076
1284
  """
1077
1285
  Get detailed information for a wallet including balance and transaction summary.
@@ -1079,57 +1287,61 @@ class LunaWallet:
1079
1287
  """
1080
1288
  if address is None:
1081
1289
  address = self.current_wallet_address
1082
-
1290
+
1083
1291
  if not address:
1084
1292
  return None
1085
-
1293
+
1086
1294
  try:
1087
1295
  from .wallet_manager import get_wallet_manager
1296
+
1088
1297
  state_manager = get_wallet_manager()
1089
-
1298
+
1090
1299
  summary = state_manager.get_wallet_summary(address)
1091
1300
  if summary:
1092
1301
  return summary
1093
-
1302
+
1094
1303
  # Fallback to basic wallet info
1095
1304
  if address in self.wallets:
1096
1305
  wallet_data = self.wallets[address]
1097
1306
  return {
1098
- 'address': address,
1099
- 'label': wallet_data.get('label', 'Wallet'),
1100
- 'balance': wallet_data.get('balance', 0.0),
1101
- 'available_balance': wallet_data.get('available_balance', 0.0),
1102
- 'is_locked': wallet_data.get('is_locked', True),
1307
+ "address": address,
1308
+ "label": wallet_data.get("label", "Wallet"),
1309
+ "balance": wallet_data.get("balance", 0.0),
1310
+ "available_balance": wallet_data.get("available_balance", 0.0),
1311
+ "is_locked": wallet_data.get("is_locked", True),
1103
1312
  }
1104
-
1313
+
1105
1314
  return None
1106
-
1315
+
1107
1316
  except Exception as e:
1108
1317
  print(f"⚠️ Error getting wallet details: {e}")
1109
1318
  return None
1110
-
1111
- def get_wallet_transactions(self, address: str = None, tx_type: str = 'all') -> List[Dict]:
1319
+
1320
+ def get_wallet_transactions(
1321
+ self, address: str = None, tx_type: str = "all"
1322
+ ) -> List[Dict]:
1112
1323
  """
1113
1324
  Get transactions for a wallet from the state manager.
1114
-
1325
+
1115
1326
  tx_type: 'all', 'confirmed', 'pending', 'transfers', 'rewards', 'genesis'
1116
1327
  """
1117
1328
  if address is None:
1118
1329
  address = self.current_wallet_address
1119
-
1330
+
1120
1331
  if not address:
1121
1332
  return []
1122
-
1333
+
1123
1334
  try:
1124
1335
  from .wallet_manager import get_wallet_manager
1336
+
1125
1337
  state_manager = get_wallet_manager()
1126
-
1338
+
1127
1339
  return state_manager.get_transactions(address, tx_type)
1128
-
1340
+
1129
1341
  except Exception as e:
1130
1342
  print(f"⚠️ Error getting transactions: {e}")
1131
1343
  return []
1132
-
1344
+
1133
1345
  def register_wallet_ui_callback(self, callback: Callable) -> None:
1134
1346
  """
1135
1347
  Register a callback to receive real-time wallet balance updates.
@@ -1137,12 +1349,15 @@ class LunaWallet:
1137
1349
  """
1138
1350
  try:
1139
1351
  from .wallet_manager import get_wallet_manager
1352
+
1140
1353
  state_manager = get_wallet_manager()
1141
1354
  state_manager.on_balance_update(callback)
1142
1355
  except Exception as e:
1143
1356
  print(f"⚠️ Error registering callback: {e}")
1144
-
1145
- def start_continuous_sync(self, blockchain=None, mempool=None, poll_interval: int = 30) -> None:
1357
+
1358
+ def start_continuous_sync(
1359
+ self, blockchain=None, mempool=None, poll_interval: int = 30
1360
+ ) -> None:
1146
1361
  """
1147
1362
  Start continuous synchronization in background thread.
1148
1363
  Syncs every poll_interval seconds.
@@ -1150,168 +1365,189 @@ class LunaWallet:
1150
1365
  if not blockchain or not mempool:
1151
1366
  print("❌ blockchain and mempool instances required")
1152
1367
  return
1153
-
1368
+
1154
1369
  try:
1155
1370
  from .wallet_manager import get_wallet_manager
1156
1371
  from .wallet_sync_helper import WalletSyncHelper
1157
-
1372
+
1158
1373
  state_manager = get_wallet_manager()
1159
-
1374
+
1160
1375
  # Register wallets
1161
1376
  addresses = list(self.wallets.keys())
1162
1377
  state_manager.register_wallets(addresses)
1163
-
1378
+
1164
1379
  def sync_callback(balance_data):
1165
1380
  """Update LunaWallet when state manager updates"""
1166
1381
  for address, balance_info in balance_data.items():
1167
1382
  if address in self.wallets:
1168
- self.wallets[address]['balance'] = balance_info['confirmed_balance']
1169
- self.wallets[address]['available_balance'] = balance_info['available_balance']
1170
-
1383
+ self.wallets[address]["balance"] = balance_info[
1384
+ "confirmed_balance"
1385
+ ]
1386
+ self.wallets[address]["available_balance"] = balance_info[
1387
+ "available_balance"
1388
+ ]
1389
+
1171
1390
  if address == self.current_wallet_address:
1172
- self.balance = balance_info['confirmed_balance']
1173
- self.available_balance = balance_info['available_balance']
1174
-
1391
+ self.balance = balance_info["confirmed_balance"]
1392
+ self.available_balance = balance_info["available_balance"]
1393
+
1175
1394
  # Create sync helper
1176
1395
  sync_helper = WalletSyncHelper(self, blockchain, mempool)
1177
-
1396
+
1178
1397
  # Start continuous sync with callback
1179
1398
  sync_helper.start_continuous_sync(
1180
- poll_interval,
1181
- on_balance_update=sync_callback
1399
+ poll_interval, on_balance_update=sync_callback
1182
1400
  )
1183
-
1401
+
1184
1402
  print(f"🔄 Started continuous sync (interval: {poll_interval}s)")
1185
-
1403
+
1186
1404
  except Exception as e:
1187
1405
  print(f"❌ Error starting continuous sync: {e}")
1188
-
1406
+
1189
1407
  def save_to_file(self, filename=None):
1190
1408
  """Save wallet to file"""
1191
1409
  if not self.data_dir:
1192
1410
  return False
1193
-
1411
+
1194
1412
  if filename is None:
1195
1413
  filename = f"wallet_{self.address}.json"
1196
-
1414
+
1197
1415
  filepath = os.path.join(self.data_dir, filename)
1198
-
1416
+
1199
1417
  try:
1200
1418
  os.makedirs(self.data_dir, exist_ok=True)
1201
-
1419
+
1202
1420
  encrypted_key_data = None
1203
1421
  if self.encrypted_private_key:
1204
1422
  if isinstance(self.encrypted_private_key, bytes):
1205
- encrypted_key_data = base64.b64encode(self.encrypted_private_key).decode('utf-8')
1423
+ encrypted_key_data = base64.b64encode(
1424
+ self.encrypted_private_key
1425
+ ).decode("utf-8")
1206
1426
  else:
1207
- encrypted_key_data = base64.b64encode(self.encrypted_private_key.encode()).decode('utf-8')
1208
-
1427
+ encrypted_key_data = base64.b64encode(
1428
+ self.encrypted_private_key.encode()
1429
+ ).decode("utf-8")
1430
+
1209
1431
  serializable_wallets = {}
1210
1432
  for addr, wallet_info in self.wallets.items():
1211
1433
  serializable_wallet = wallet_info.copy()
1212
- if serializable_wallet.get('encrypted_private_key') and isinstance(serializable_wallet['encrypted_private_key'], bytes):
1213
- serializable_wallet['encrypted_private_key'] = base64.b64encode(
1214
- serializable_wallet['encrypted_private_key']
1215
- ).decode('utf-8')
1434
+ if serializable_wallet.get("encrypted_private_key") and isinstance(
1435
+ serializable_wallet["encrypted_private_key"], bytes
1436
+ ):
1437
+ serializable_wallet["encrypted_private_key"] = base64.b64encode(
1438
+ serializable_wallet["encrypted_private_key"]
1439
+ ).decode("utf-8")
1216
1440
  serializable_wallets[addr] = serializable_wallet
1217
-
1441
+
1218
1442
  wallet_data = {
1219
- 'address': self.address,
1220
- 'balance': self.balance,
1221
- 'available_balance': self.available_balance,
1222
- 'created': self.created,
1223
- 'public_key': self.public_key,
1224
- 'encrypted_private_key': encrypted_key_data,
1225
- 'label': self.label,
1226
- 'is_locked': self.is_locked,
1227
- 'wallets': serializable_wallets,
1228
- 'current_wallet_address': self.current_wallet_address,
1229
- 'crypto_standard': 'SM2_GB/T_32918'
1443
+ "address": self.address,
1444
+ "balance": self.balance,
1445
+ "available_balance": self.available_balance,
1446
+ "created": self.created,
1447
+ "public_key": self.public_key,
1448
+ "encrypted_private_key": encrypted_key_data,
1449
+ "label": self.label,
1450
+ "is_locked": self.is_locked,
1451
+ "wallets": serializable_wallets,
1452
+ "current_wallet_address": self.current_wallet_address,
1453
+ "crypto_standard": "SM2_GB/T_32918",
1230
1454
  }
1231
-
1232
- with open(filepath, 'w') as f:
1455
+
1456
+ with open(filepath, "w") as f:
1233
1457
  json.dump(wallet_data, f, indent=2)
1234
-
1458
+
1235
1459
  print(f"DEBUG: Wallet saved to {filepath}")
1236
1460
  return True
1237
1461
  except Exception as e:
1238
1462
  print(f"Error saving wallet: {e}")
1239
1463
  import traceback
1464
+
1240
1465
  traceback.print_exc()
1241
1466
  return False
1242
-
1467
+
1243
1468
  def load_from_file(self, filename, password=None):
1244
1469
  """Load wallet from file"""
1245
1470
  if not self.data_dir:
1246
1471
  return False
1247
-
1472
+
1248
1473
  filepath = os.path.join(self.data_dir, filename)
1249
-
1474
+
1250
1475
  try:
1251
- with open(filepath, 'r') as f:
1476
+ with open(filepath, "r") as f:
1252
1477
  wallet_data = json.load(f)
1253
-
1478
+
1254
1479
  # Check crypto standard
1255
- crypto_standard = wallet_data.get('crypto_standard')
1256
- if crypto_standard != 'SM2_GB/T_32918':
1257
- print(f"DEBUG: WARNING: Loading wallet with different crypto standard: {crypto_standard}")
1258
-
1480
+ crypto_standard = wallet_data.get("crypto_standard")
1481
+ if crypto_standard != "SM2_GB/T_32918":
1482
+ print(
1483
+ f"DEBUG: WARNING: Loading wallet with different crypto standard: {crypto_standard}"
1484
+ )
1485
+
1259
1486
  # Load wallets collection
1260
- self.wallets = wallet_data.get('wallets', {})
1261
-
1487
+ self.wallets = wallet_data.get("wallets", {})
1488
+
1262
1489
  # Load current wallet address
1263
- self.current_wallet_address = wallet_data.get('current_wallet_address')
1264
-
1490
+ self.current_wallet_address = wallet_data.get("current_wallet_address")
1491
+
1265
1492
  # If we have a current wallet, load its data
1266
- if self.current_wallet_address and self.current_wallet_address in self.wallets:
1493
+ if (
1494
+ self.current_wallet_address
1495
+ and self.current_wallet_address in self.wallets
1496
+ ):
1267
1497
  current_wallet_data = self.wallets[self.current_wallet_address]
1268
1498
  self._set_current_wallet(current_wallet_data)
1269
-
1270
- encrypted_key = wallet_data.get('encrypted_private_key')
1499
+
1500
+ encrypted_key = wallet_data.get("encrypted_private_key")
1271
1501
  if encrypted_key:
1272
- self.encrypted_private_key = base64.b64decode(encrypted_key.encode())
1502
+ self.encrypted_private_key = base64.b64decode(
1503
+ encrypted_key.encode()
1504
+ )
1273
1505
  if self.current_wallet_address in self.wallets:
1274
- self.wallets[self.current_wallet_address]['encrypted_private_key'] = self.encrypted_private_key
1275
-
1506
+ self.wallets[self.current_wallet_address][
1507
+ "encrypted_private_key"
1508
+ ] = self.encrypted_private_key
1509
+
1276
1510
  self.refresh_balance()
1277
-
1511
+
1278
1512
  if password and self.encrypted_private_key and self.current_wallet_address:
1279
1513
  return self.unlock_wallet(self.current_wallet_address, password)
1280
-
1514
+
1281
1515
  print(f"DEBUG: Wallet loaded from {filepath}")
1282
1516
  print(f"DEBUG: Total wallets: {len(self.wallets)}")
1283
1517
  print(f"DEBUG: Current wallet: {self.current_wallet_address}")
1284
-
1518
+
1285
1519
  return True
1286
1520
  except Exception as e:
1287
1521
  print(f"Error loading wallet: {e}")
1288
1522
  return False
1289
-
1523
+
1290
1524
  def debug_crypto_info(self):
1291
1525
  """Debug cryptographic information"""
1292
- print("\n" + "="*60)
1526
+ print("\n" + "=" * 60)
1293
1527
  print("SM2 CRYPTOGRAPHY DEBUG INFO")
1294
- print("="*60)
1295
-
1528
+ print("=" * 60)
1529
+
1296
1530
  if self.current_wallet_address:
1297
1531
  print(f"Current Address: {self.address}")
1298
-
1532
+
1299
1533
  # Check address format
1300
- if self.address.startswith('LUN_'):
1534
+ if self.address.startswith("LUN_"):
1301
1535
  print("✅ Address has correct LUN_ prefix")
1302
1536
  else:
1303
1537
  print("❌ Address missing LUN_ prefix")
1304
-
1538
+
1305
1539
  # Check private key
1306
1540
  if self.private_key:
1307
1541
  print(f"Private Key: {self.private_key[:16]}...")
1308
1542
  if len(self.private_key) == 64:
1309
1543
  print(f"✅ Private key is 256-bit (64 hex chars)")
1310
1544
  else:
1311
- print(f"❌ Private key has wrong length: {len(self.private_key)} chars")
1545
+ print(
1546
+ f"❌ Private key has wrong length: {len(self.private_key)} chars"
1547
+ )
1312
1548
  else:
1313
1549
  print("❌ No private key available")
1314
-
1550
+
1315
1551
  # Check public key
1316
1552
  if self.public_key:
1317
1553
  print(f"Public Key: {self.public_key[:16]}...")
@@ -1321,14 +1557,14 @@ class LunaWallet:
1321
1557
  print(f"❌ Public key too short: {len(self.public_key)} chars")
1322
1558
  else:
1323
1559
  print("❌ No public key available")
1324
-
1560
+
1325
1561
  # Check cryptographic standard
1326
1562
  wallet_data = self.wallets.get(self.address, {})
1327
- if wallet_data.get('crypto_standard') == 'SM2_GB/T_32918':
1563
+ if wallet_data.get("crypto_standard") == "SM2_GB/T_32918":
1328
1564
  print("✅ Using SM2 GB/T 32918 East Asian cryptography standard")
1329
1565
  else:
1330
1566
  print("❌ Not using SM2 cryptography standard")
1331
-
1567
+
1332
1568
  # Test signing
1333
1569
  if self.private_key and not self.is_locked:
1334
1570
  test_sig = self._sign_transaction_data({"test": "data"})
@@ -1338,5 +1574,5 @@ class LunaWallet:
1338
1574
  print("❌ Cannot sign transactions")
1339
1575
  else:
1340
1576
  print("❌ No wallet selected")
1341
-
1342
- print("="*60)
1577
+
1578
+ print("=" * 60)