lunalib 1.7.3__py3-none-any.whl → 1.8.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.
Files changed (52) hide show
  1. lunalib/.gitignore +3 -0
  2. lunalib/__pycache__/__init__.cpython-310.pyc +0 -0
  3. lunalib/core/__pycache__/__init__.cpython-310.pyc +0 -0
  4. lunalib/core/__pycache__/blockchain.cpython-310.pyc +0 -0
  5. lunalib/core/__pycache__/crypto.cpython-310.pyc +0 -0
  6. lunalib/core/__pycache__/mempool.cpython-310.pyc +0 -0
  7. lunalib/core/__pycache__/wallet.cpython-310.pyc +0 -0
  8. lunalib/core/blockchain.py +13 -16
  9. lunalib/core/mempool.py +1 -0
  10. lunalib/core/wallet.py +121 -24
  11. lunalib/core/wallet_db.py +48 -47
  12. lunalib/gtx/__pycache__/__init__.cpython-310.pyc +0 -0
  13. lunalib/gtx/__pycache__/bill_registry.cpython-310.pyc +0 -0
  14. lunalib/gtx/__pycache__/digital_bill.cpython-310.pyc +0 -0
  15. lunalib/gtx/__pycache__/genesis.cpython-310.pyc +0 -0
  16. lunalib/mining/__pycache__/__init__.cpython-310.pyc +0 -0
  17. lunalib/mining/__pycache__/cuda_manager.cpython-310.pyc +0 -0
  18. lunalib/mining/__pycache__/difficulty.cpython-310.pyc +0 -0
  19. lunalib/mining/__pycache__/miner.cpython-310.pyc +0 -0
  20. lunalib/storage/__pycache__/__init__.cpython-310.pyc +0 -0
  21. lunalib/storage/__pycache__/cache.cpython-310.pyc +0 -0
  22. lunalib/storage/__pycache__/database.cpython-310.pyc +0 -0
  23. lunalib/storage/__pycache__/encryption.cpython-310.pyc +0 -0
  24. lunalib/tests/__pycache__/conftest.cpython-310-pytest-9.0.1.pyc +0 -0
  25. lunalib/tests/__pycache__/test_blockchain.cpython-310-pytest-9.0.1.pyc +0 -0
  26. lunalib/tests/__pycache__/test_crypto.cpython-310-pytest-9.0.1.pyc +0 -0
  27. lunalib/tests/__pycache__/test_gtx.cpython-310-pytest-9.0.1.pyc +0 -0
  28. lunalib/tests/__pycache__/test_mining.cpython-310-pytest-9.0.1.pyc +0 -0
  29. lunalib/tests/__pycache__/test_storage.cpython-310-pytest-9.0.1.pyc +0 -0
  30. lunalib/tests/__pycache__/test_transactions.cpython-310-pytest-9.0.1.pyc +0 -0
  31. lunalib/tests/__pycache__/test_wallet.cpython-310-pytest-9.0.1.pyc +0 -0
  32. lunalib/tests/conftest.py +41 -0
  33. lunalib/tests/init.py +0 -0
  34. lunalib/tests/integration/__pycache__/test_integration.cpython-310-pytest-9.0.1.pyc +0 -0
  35. lunalib/tests/integration/test_integration.py +62 -0
  36. lunalib/tests/test_blockchain.py +34 -0
  37. lunalib/tests/test_crypto.py +42 -0
  38. lunalib/tests/test_gtx.py +135 -0
  39. lunalib/tests/test_mining.py +244 -0
  40. lunalib/tests/test_security_suite.py +832 -0
  41. lunalib/tests/test_storage.py +84 -0
  42. lunalib/tests/test_transactions.py +103 -0
  43. lunalib/tests/test_wallet.py +91 -0
  44. lunalib/transactions/__pycache__/__init__.cpython-310.pyc +0 -0
  45. lunalib/transactions/__pycache__/security.cpython-310.pyc +0 -0
  46. lunalib/transactions/__pycache__/transactions.cpython-310.pyc +0 -0
  47. lunalib/transactions/__pycache__/validator.cpython-310.pyc +0 -0
  48. {lunalib-1.7.3.dist-info → lunalib-1.8.0.dist-info}/METADATA +1 -1
  49. lunalib-1.8.0.dist-info/RECORD +77 -0
  50. lunalib-1.7.3.dist-info/RECORD +0 -34
  51. {lunalib-1.7.3.dist-info → lunalib-1.8.0.dist-info}/WHEEL +0 -0
  52. {lunalib-1.7.3.dist-info → lunalib-1.8.0.dist-info}/top_level.txt +0 -0
lunalib/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .venv
2
+ build
3
+ .__pycache__
@@ -1,3 +1,4 @@
1
+ from tqdm import tqdm
1
2
  # blockchain.py - Updated version
2
3
 
3
4
  from ..storage.cache import BlockchainCache
@@ -267,12 +268,12 @@ class BlockchainManager:
267
268
  def get_blocks_range(self, start_height: int, end_height: int) -> List[Dict]:
268
269
  """Get range of blocks"""
269
270
  blocks = []
270
-
271
+
271
272
  # Check cache first
272
273
  cached_blocks = self.cache.get_block_range(start_height, end_height)
273
274
  if len(cached_blocks) == (end_height - start_height + 1):
274
275
  return cached_blocks
275
-
276
+
276
277
  try:
277
278
  response = requests.get(
278
279
  f'{self.endpoint_url}/blockchain/range?start={start_height}&end={end_height}',
@@ -286,15 +287,15 @@ class BlockchainManager:
286
287
  self.cache.save_block(height, block.get('hash', ''), block)
287
288
  else:
288
289
  # Fallback: get blocks individually
289
- for height in range(start_height, end_height + 1):
290
+ for height in tqdm(range(start_height, end_height + 1), desc="Get Blocks", leave=False):
290
291
  block = self.get_block(height)
291
292
  if block:
292
293
  blocks.append(block)
293
294
  time.sleep(0.01) # Be nice to the API
294
-
295
+
295
296
  except Exception as e:
296
297
  print(f"Get blocks range error: {e}")
297
-
298
+
298
299
  return blocks
299
300
 
300
301
  def get_mempool(self) -> List[Dict]:
@@ -322,22 +323,19 @@ class BlockchainManager:
322
323
  """Scan blockchain for transactions involving an address"""
323
324
  if end_height is None:
324
325
  end_height = self.get_blockchain_height()
325
-
326
+
326
327
  print(f"[SCAN] Scanning transactions for {address} from block {start_height} to {end_height}")
327
-
328
+
328
329
  transactions = []
329
-
330
- # Scan in batches for efficiency
331
330
  batch_size = 100
332
- for batch_start in range(start_height, end_height + 1, batch_size):
331
+ total_batches = ((end_height - start_height) // batch_size) + 1
332
+ for batch_start in tqdm(range(start_height, end_height + 1, batch_size), desc=f"Scan {address}", total=total_batches):
333
333
  batch_end = min(batch_start + batch_size - 1, end_height)
334
- print(f"[SCAN] Processing batch {batch_start}-{batch_end}...")
335
334
  blocks = self.get_blocks_range(batch_start, batch_end)
336
-
337
335
  for block in blocks:
338
336
  block_transactions = self._find_address_transactions(block, address)
339
337
  transactions.extend(block_transactions)
340
-
338
+
341
339
  print(f"[SCAN] Found {len(transactions)} total transactions for {address}")
342
340
  return transactions
343
341
 
@@ -389,11 +387,10 @@ class BlockchainManager:
389
387
  results: Dict[str, List[Dict]] = {addr: [] for addr in addresses}
390
388
 
391
389
  batch_size = 100
392
- for batch_start in range(start_height, end_height + 1, batch_size):
390
+ total_batches = ((end_height - start_height) // batch_size) + 1
391
+ for batch_start in tqdm(range(start_height, end_height + 1, batch_size), desc="Multi-Scan", total=total_batches):
393
392
  batch_end = min(batch_start + batch_size - 1, end_height)
394
- print(f"[MULTI-SCAN] Processing batch {batch_start}-{batch_end}...")
395
393
  blocks = self.get_blocks_range(batch_start, batch_end)
396
-
397
394
  for block in blocks:
398
395
  collected = self._collect_transactions_for_addresses(block, normalized_map)
399
396
  for original_addr, txs in collected.items():
lunalib/core/mempool.py CHANGED
@@ -211,6 +211,7 @@ class MempoolManager:
211
211
  to_norm = self._normalize_address(tx.get('to') or tx.get('receiver'))
212
212
  if target_norm and (from_norm == target_norm or to_norm == target_norm):
213
213
  transactions.append(tx)
214
+ print(f"[MEMPOOL] get_pending_transactions for {address}: {len(transactions)} txs returned")
214
215
  return transactions
215
216
 
216
217
  def get_pending_transactions_for_addresses(self, addresses: List[str], fetch_remote: bool = True) -> Dict[str, List[Dict]]:
lunalib/core/wallet.py CHANGED
@@ -1,3 +1,37 @@
1
+ def update_all_balances(self):
2
+ """Unified balance update: recalculates and persists balances."""
3
+ # 残高計算前にキャッシュを最新化
4
+ self._refresh_tx_caches()
5
+ available = self.calculate_available_balance()
6
+ # Optionally, persist to DB if needed
7
+ if self.current_wallet_address in self.wallets:
8
+ w = self.wallets[self.current_wallet_address]
9
+ if hasattr(self, 'db') and self.db:
10
+ self.db.save_wallet(
11
+ self.current_wallet_address,
12
+ w.get("label", ""),
13
+ w.get("public_key", ""),
14
+ w.get("encrypted_private_key", b""),
15
+ w.get("is_locked", True),
16
+ w.get("created", 0),
17
+ self.balance,
18
+ available
19
+ )
20
+ print(f"[WALLET] update_all_balances: balance={self.balance}, available={self.available_balance}")
21
+
22
+ def _refresh_tx_caches(self):
23
+ """Force refresh of confirmed and pending tx caches for the current wallet."""
24
+ if not self.current_wallet_address:
25
+ return
26
+ from lunalib.core.blockchain import BlockchainManager
27
+ from lunalib.core.mempool import MempoolManager
28
+ blockchain = BlockchainManager()
29
+ mempool = MempoolManager()
30
+ confirmed = blockchain.scan_transactions_for_address(self.current_wallet_address)
31
+ pending = mempool.get_pending_transactions(self.current_wallet_address, fetch_remote=True)
32
+ self._confirmed_tx_cache[self.current_wallet_address] = confirmed
33
+ self._pending_tx_cache[self.current_wallet_address] = pending
34
+
1
35
  import time
2
36
  import hashlib
3
37
  import json
@@ -16,6 +50,7 @@ from .wallet_db import WalletDB
16
50
  class LunaWallet:
17
51
  def __init__(self, data_dir=None):
18
52
  self.data_dir = data_dir or os.path.expanduser("~/.lunawallet")
53
+ print(f"[LunaWallet] data_dir: {self.data_dir}")
19
54
  self.db = WalletDB(self.data_dir)
20
55
  self.wallets = {} # address -> wallet dict (in-memory cache)
21
56
  self.current_wallet_address = None
@@ -399,16 +434,19 @@ class LunaWallet:
399
434
  def calculate_available_balance(self) -> float:
400
435
  """Calculate available balance (total balance minus pending outgoing transactions)"""
401
436
  try:
402
- # Get total balance from blockchain
403
- total_balance = self._get_total_balance_from_blockchain()
437
+ from lunalib.core.blockchain import BlockchainManager
438
+ blockchain = BlockchainManager()
439
+ current_height = blockchain.get_blockchain_height()
404
440
 
405
- # Get pending outgoing amount
406
- pending_outgoing = self._get_pending_balance()
441
+ # Get confirmed transactions for this wallet
442
+ confirmed = self._confirmed_tx_cache.get(self.address, [])
443
+ total_balance = self._compute_confirmed_balance(confirmed, current_height)
407
444
 
408
- # Also check for any incoming pending transactions
409
- incoming_pending = self._get_pending_incoming_balance()
445
+ # Get pending transactions for this wallet
446
+ pending = self._pending_tx_cache.get(self.address, [])
447
+ pending_out, pending_in = self._compute_pending_totals(pending, self.address, current_height)
410
448
 
411
- available_balance = max(0.0, total_balance - pending_outgoing)
449
+ available_balance = max(0.0, total_balance - pending_out)
412
450
 
413
451
  # Update both current wallet and wallets collection
414
452
  self.available_balance = available_balance
@@ -421,11 +459,11 @@ class LunaWallet:
421
459
  self.wallets[self.current_wallet_address]["balance"] = total_balance
422
460
 
423
461
  print(
424
- f"DEBUG: Balance calculated - Total: {total_balance}, Pending Out: {pending_outgoing}, Available: {available_balance}"
462
+ f"DEBUG: Balance calculated - Total: {total_balance}, Pending Out: {pending_out}, Pending In: {pending_in}, Available: {available_balance}"
425
463
  )
426
464
 
427
- if incoming_pending > 0:
428
- print(f"DEBUG: Also {incoming_pending} LUN incoming (pending)")
465
+ if pending_in > 0:
466
+ print(f"DEBUG: Also {pending_in} LUN incoming (pending)")
429
467
 
430
468
  return available_balance
431
469
 
@@ -434,15 +472,31 @@ class LunaWallet:
434
472
  return self.balance # Fallback to total balance
435
473
 
436
474
  def _compute_confirmed_balance(self, transactions: List[Dict]) -> float:
437
- """Compute confirmed balance from a list of transactions."""
475
+ """Compute confirmed balance from a list of transactions, using confirmation count for rewards."""
438
476
  total_balance = 0.0
477
+ current_height = None
478
+ import inspect
479
+ # Try to get current_height from caller if passed
480
+ frame = inspect.currentframe().f_back
481
+ if frame and 'current_height' in frame.f_locals:
482
+ current_height = frame.f_locals['current_height']
439
483
  for tx in transactions:
440
484
  tx_type = (tx.get("type") or "").lower()
441
485
  direction = tx.get("direction", "")
442
486
  amount = float(tx.get("amount", 0) or 0)
487
+ block_height = tx.get("block_height")
488
+ confirmations = None
489
+ if block_height is not None and current_height is not None:
490
+ try:
491
+ confirmations = int(current_height) - int(block_height) + 1
492
+ except Exception:
493
+ confirmations = None
443
494
 
444
495
  if tx_type == "reward" or tx.get("from") == "network":
445
- total_balance += amount
496
+ print(f"[DEBUG] reward tx: {tx}, confirmations={confirmations}")
497
+ # block_heightやcurrent_heightがNoneならとりあえず加算
498
+ if confirmations is None or confirmations >= 6:
499
+ total_balance += amount
446
500
  elif direction == "incoming":
447
501
  total_balance += amount
448
502
  elif direction == "outgoing":
@@ -450,12 +504,13 @@ class LunaWallet:
450
504
  total_balance -= amount
451
505
  total_balance -= fee
452
506
 
507
+ print(f"[DEBUG] computed confirmed balance: {total_balance}")
453
508
  return max(0.0, total_balance)
454
509
 
455
510
  def _compute_pending_totals(
456
- self, pending_txs: List[Dict], address: str
511
+ self, pending_txs: List[Dict], address: str, current_height: int = None
457
512
  ) -> Tuple[float, float]:
458
- """Return (pending_outgoing, pending_incoming) for an address."""
513
+ """Return (pending_outgoing, pending_incoming) for an address, including pending rewards (<6 confs)."""
459
514
  pending_out = 0.0
460
515
  pending_in = 0.0
461
516
 
@@ -463,14 +518,26 @@ class LunaWallet:
463
518
  for tx in pending_txs:
464
519
  from_norm = self._normalize_address(tx.get("from") or tx.get("sender"))
465
520
  to_norm = self._normalize_address(tx.get("to") or tx.get("receiver"))
521
+ tx_type = (tx.get("type") or "").lower()
522
+ amount = float(tx.get("amount", 0) or 0)
523
+ block_height = tx.get("block_height")
524
+ confirmations = None
525
+ if block_height is not None and current_height is not None:
526
+ try:
527
+ confirmations = int(current_height) - int(block_height) + 1
528
+ except Exception:
529
+ confirmations = None
466
530
 
467
531
  if from_norm == target_norm:
468
- amount = float(tx.get("amount", 0) or 0)
469
532
  fee = float(tx.get("fee", 0) or tx.get("gas", 0) or 0)
470
533
  pending_out += amount + fee
471
534
  if to_norm == target_norm:
472
- amount = float(tx.get("amount", 0) or 0)
473
- pending_in += amount
535
+ # For rewards, only count as pending if confirmations < 6
536
+ if tx_type == "reward" or tx.get("from") == "network":
537
+ if confirmations is not None and confirmations < 6:
538
+ pending_in += amount
539
+ else:
540
+ pending_in += amount
474
541
 
475
542
  return pending_out, pending_in
476
543
 
@@ -503,7 +570,7 @@ class LunaWallet:
503
570
  return updated
504
571
 
505
572
  def refresh_all_wallet_balances(self) -> Dict[str, Dict[str, float]]:
506
- """Scan blockchain once for all wallets, then update balances with pending mempool data."""
573
+ """Scan blockchain and mempool for all wallets, update balances and pending info."""
507
574
  addresses = list(self.wallets.keys())
508
575
  if not addresses:
509
576
  return {}
@@ -516,15 +583,44 @@ class LunaWallet:
516
583
  mempool = MempoolManager()
517
584
 
518
585
  confirmed_map = blockchain.scan_transactions_for_addresses(addresses)
519
- pending_map = mempool.get_pending_transactions_for_addresses(
520
- addresses, fetch_remote=True
521
- )
586
+ pending_map = mempool.get_pending_transactions_for_addresses(addresses, fetch_remote=True)
522
587
 
523
588
  # Reset caches with the latest snapshots
524
589
  self._confirmed_tx_cache = confirmed_map
525
590
  self._pending_tx_cache = pending_map
526
591
 
527
- return self._recompute_balances_from_cache()
592
+ # Recompute balances and pending for all wallets
593
+ updated: Dict[str, Dict[str, float]] = {}
594
+ for addr in addresses:
595
+ confirmed = confirmed_map.get(addr, [])
596
+ pending = pending_map.get(addr, [])
597
+ # 最新のブロック高を取得
598
+ try:
599
+ current_height = blockchain.get_blockchain_height()
600
+ except Exception:
601
+ current_height = None
602
+ total_balance = max(0.0, self._compute_confirmed_balance(confirmed))
603
+ pending_out, pending_in = self._compute_pending_totals(pending, addr, current_height)
604
+ available_balance = max(0.0, total_balance - pending_out)
605
+
606
+ wallet_data = self.wallets.get(addr, {})
607
+ wallet_data["balance"] = total_balance
608
+ wallet_data["available_balance"] = available_balance
609
+ wallet_data["pending_out"] = pending_out
610
+ wallet_data["pending_in"] = pending_in
611
+
612
+ if addr == self.current_wallet_address:
613
+ self.balance = total_balance
614
+ self.available_balance = available_balance
615
+
616
+ updated[addr] = {
617
+ "balance": total_balance,
618
+ "available_balance": available_balance,
619
+ "pending_out": pending_out,
620
+ "pending_in": pending_in,
621
+ }
622
+
623
+ return updated
528
624
 
529
625
  except Exception as e:
530
626
  print(f"DEBUG: Error refreshing all wallet balances: {e}")
@@ -543,11 +639,12 @@ class LunaWallet:
543
639
  confirmed = self._confirmed_tx_cache.get(addr, [])
544
640
  pending = self._pending_tx_cache.get(addr, [])
545
641
 
642
+ # pending_in/pending_outはwallet_dataに格納済み
546
643
  overview[addr] = {
547
644
  "balance": wallet_data.get("balance", 0.0),
548
645
  "available_balance": wallet_data.get("available_balance", 0.0),
549
- "pending_out": self._compute_pending_totals(pending, addr)[0],
550
- "pending_in": self._compute_pending_totals(pending, addr)[1],
646
+ "pending_out": wallet_data.get("pending_out", 0.0),
647
+ "pending_in": wallet_data.get("pending_in", 0.0),
551
648
  }
552
649
 
553
650
  if include_transactions:
lunalib/core/wallet_db.py CHANGED
@@ -1,64 +1,65 @@
1
- import sqlite3
2
- import os
3
- from cryptography.fernet import Fernet
4
- import hashlib
5
- import base64
6
- import json
7
- import time
1
+
2
+ from lunalib.storage.database import WalletDatabase
3
+
8
4
 
9
5
  class WalletDB:
10
6
  def __init__(self, data_dir=None):
7
+ # data_dir is kept for compatibility, but db_path is passed to WalletDatabase
8
+ import os
11
9
  self.data_dir = data_dir or os.path.expanduser("~/.lunawallet")
12
10
  os.makedirs(self.data_dir, exist_ok=True)
13
11
  self.db_path = os.path.join(self.data_dir, "wallets.db")
14
- self.conn = sqlite3.connect(self.db_path)
15
- self._init_db()
16
-
17
- def _init_db(self):
18
- c = self.conn.cursor()
19
- c.execute('''CREATE TABLE IF NOT EXISTS wallets (
20
- address TEXT PRIMARY KEY,
21
- label TEXT,
22
- public_key TEXT,
23
- encrypted_private_key BLOB,
24
- is_locked INTEGER,
25
- created REAL,
26
- balance REAL,
27
- available_balance REAL
28
- )''')
29
- self.conn.commit()
12
+ print(f"[WalletDB] data_dir: {self.data_dir}")
13
+ print(f"[WalletDB] db_path: {self.db_path}")
14
+ self.db = WalletDatabase(db_path=self.db_path)
30
15
 
31
16
  def save_wallet(self, address, label, public_key, encrypted_private_key, is_locked, created, balance, available_balance):
32
- c = self.conn.cursor()
33
- c.execute('''REPLACE INTO wallets (address, label, public_key, encrypted_private_key, is_locked, created, balance, available_balance)
34
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
35
- (address, label, public_key, encrypted_private_key, int(is_locked), created, balance, available_balance))
36
- self.conn.commit()
17
+ # Compose wallet_data dict for WalletDatabase
18
+ wallet_data = {
19
+ 'address': address,
20
+ 'label': label,
21
+ 'public_key': public_key,
22
+ 'encrypted_private_key': encrypted_private_key,
23
+ 'balance': balance,
24
+ 'created': created,
25
+ 'metadata': {'is_locked': bool(is_locked), 'available_balance': available_balance}
26
+ }
27
+ return self.db.save_wallet(wallet_data)
37
28
 
38
29
  def load_wallet(self, address):
39
- c = self.conn.cursor()
40
- c.execute('SELECT * FROM wallets WHERE address=?', (address,))
41
- row = c.fetchone()
42
- if row:
43
- return {
44
- 'address': row[0],
45
- 'label': row[1],
46
- 'public_key': row[2],
47
- 'encrypted_private_key': row[3],
48
- 'is_locked': bool(row[4]),
49
- 'created': row[5],
50
- 'balance': row[6],
51
- 'available_balance': row[7],
52
- }
53
- return None
30
+ w = self.db.load_wallet(address)
31
+ if w:
32
+ # For compatibility, flatten metadata fields
33
+ meta = w.get('metadata', {})
34
+ w['is_locked'] = meta.get('is_locked', False)
35
+ w['available_balance'] = meta.get('available_balance', 0.0)
36
+ return w
54
37
 
55
38
  def list_wallets(self):
56
- c = self.conn.cursor()
57
- c.execute('SELECT address, label, is_locked, balance, available_balance FROM wallets')
58
- return c.fetchall()
39
+ # Return list of tuples for compatibility: (address, label, is_locked, balance, available_balance)
40
+ import sqlite3
41
+ conn = sqlite3.connect(self.db_path)
42
+ cursor = conn.cursor()
43
+ cursor.execute('SELECT address, label, metadata, balance FROM wallets')
44
+ rows = cursor.fetchall()
45
+ conn.close()
46
+ result = []
47
+ for row in rows:
48
+ address, label, metadata_json, balance = row
49
+ meta = {}
50
+ try:
51
+ meta = json.loads(metadata_json) if metadata_json else {}
52
+ except Exception:
53
+ pass
54
+ is_locked = meta.get('is_locked', False)
55
+ available_balance = meta.get('available_balance', 0.0)
56
+ result.append((address, label, is_locked, balance, available_balance))
57
+ return result
58
+
59
59
 
60
60
  def close(self):
61
- self.conn.close()
61
+ # No persistent connection to close in WalletDatabase
62
+ pass
62
63
 
63
64
  # Example usage:
64
65
  if __name__ == "__main__":
@@ -0,0 +1,41 @@
1
+ import pytest
2
+ import tempfile
3
+ import os
4
+ import time
5
+ from lunalib.core.wallet import LunaWallet
6
+ from lunalib.mining.miner import GenesisMiner
7
+ from lunalib.gtx.genesis import GTXGenesis
8
+
9
+ @pytest.fixture
10
+ def temp_dir():
11
+ """Create temporary directory for test data"""
12
+ with tempfile.TemporaryDirectory() as tmpdir:
13
+ yield tmpdir
14
+
15
+ @pytest.fixture
16
+ def test_wallet(temp_dir):
17
+ """Create a test wallet"""
18
+ wallet = LunaWallet(data_dir=temp_dir)
19
+ wallet_data = wallet.create_wallet("Test Wallet", "test_password")
20
+ return wallet, wallet_data
21
+
22
+ @pytest.fixture
23
+ def test_miner():
24
+ """Create a test miner"""
25
+ return GenesisMiner()
26
+
27
+ @pytest.fixture
28
+ def test_gtx(temp_dir):
29
+ """Create test GTX system"""
30
+ return GTXGenesis()
31
+
32
+ @pytest.fixture
33
+ def sample_transaction_data(test_wallet):
34
+ """Create sample transaction data"""
35
+ wallet, wallet_data = test_wallet
36
+ return {
37
+ "from": wallet_data["address"],
38
+ "to": "LUN_test_recipient_12345",
39
+ "amount": 100.0,
40
+ "memo": "Test transaction"
41
+ }
lunalib/tests/init.py ADDED
File without changes
@@ -0,0 +1,62 @@
1
+ import pytest
2
+ import tempfile
3
+ from lunalib.core.wallet import LunaWallet
4
+ from lunalib.mining.miner import GenesisMiner
5
+ from lunalib.gtx.genesis import GTXGenesis
6
+
7
+ class TestIntegration:
8
+ def test_complete_workflow(self, temp_dir):
9
+ """Test complete wallet -> mining -> GTX workflow"""
10
+ # Create wallet
11
+ wallet = LunaWallet(data_dir=temp_dir)
12
+ wallet_data = wallet.create_wallet("Integration Test", "test_pass")
13
+
14
+ # Initialize systems
15
+ miner = GenesisMiner()
16
+ gtx = GTXGenesis()
17
+
18
+ # Mock mining a bill
19
+ with pytest.MonkeyPatch().context() as m:
20
+ m.setattr(miner, '_perform_mining', lambda *args: {
21
+ "success": True,
22
+ "hash": "000integration",
23
+ "nonce": 999,
24
+ "mining_time": 1.0
25
+ })
26
+
27
+ # Mine a bill
28
+ bill = miner.mine_bill(1000, wallet_data['address'])
29
+
30
+ assert bill['success'] is True
31
+ assert bill['denomination'] == 1000
32
+ assert bill['luna_value'] == 1000
33
+
34
+ # Create proper bill data for verification
35
+ metadata_hash = "test_metadata_hash_12345"
36
+ signature = metadata_hash # This will pass METHOD 1 verification
37
+
38
+ # Register the bill in GTX system for verification
39
+ bill_info = {
40
+ 'bill_serial': bill['bill_serial'],
41
+ 'denomination': 1000,
42
+ 'user_address': wallet_data['address'],
43
+ 'hash': "000integration",
44
+ 'mining_time': 1.0,
45
+ 'difficulty': 4,
46
+ 'luna_value': 1000,
47
+ 'timestamp': bill['timestamp'],
48
+ 'bill_data': {
49
+ 'public_key': 'test_public_key',
50
+ 'signature': signature, # Use the signature that matches metadata_hash
51
+ 'metadata_hash': metadata_hash, # This must match signature for METHOD 1
52
+ 'issued_to': wallet_data['address'],
53
+ 'front_serial': bill['bill_serial'],
54
+ 'type': 'GTX_Genesis',
55
+ 'back_serial': '' # Add this to avoid errors
56
+ }
57
+ }
58
+ gtx.bill_registry.register_bill(bill_info)
59
+
60
+ # Verify the bill
61
+ verification = gtx.verify_bill(bill['bill_serial'])
62
+ assert verification['valid'] is True
@@ -0,0 +1,34 @@
1
+ import pytest
2
+ from unittest.mock import Mock, patch
3
+ from lunalib.core.blockchain import BlockchainManager
4
+
5
+ class TestBlockchain:
6
+ @patch('lunalib.core.blockchain.requests.get')
7
+ def test_blockchain_height(self, mock_get):
8
+ """Test blockchain height retrieval"""
9
+ mock_get.return_value.status_code = 200
10
+ mock_get.return_value.json.return_value = {'height': 1500}
11
+
12
+ blockchain = BlockchainManager()
13
+ height = blockchain.get_blockchain_height()
14
+
15
+ assert height == 1500
16
+
17
+ @patch('lunalib.core.blockchain.requests.get')
18
+ def test_network_connection(self, mock_get):
19
+ """Test network connection checking"""
20
+ mock_get.return_value.status_code = 200
21
+
22
+ blockchain = BlockchainManager()
23
+ is_connected = blockchain.check_network_connection()
24
+
25
+ assert is_connected is True
26
+
27
+ def test_transaction_scanning(self):
28
+ """Test transaction scanning functionality"""
29
+ blockchain = BlockchainManager()
30
+
31
+ # This would typically be mocked in a real test
32
+ # For now, test the method exists and returns expected type
33
+ transactions = blockchain.scan_transactions_for_address("LUN_test", 0, 10)
34
+ assert isinstance(transactions, list)