lunalib 1.7.3__py3-none-any.whl → 1.7.9__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 (51) 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/mempool.py +1 -0
  9. lunalib/core/wallet.py +119 -24
  10. lunalib/core/wallet_db.py +48 -47
  11. lunalib/gtx/__pycache__/__init__.cpython-310.pyc +0 -0
  12. lunalib/gtx/__pycache__/bill_registry.cpython-310.pyc +0 -0
  13. lunalib/gtx/__pycache__/digital_bill.cpython-310.pyc +0 -0
  14. lunalib/gtx/__pycache__/genesis.cpython-310.pyc +0 -0
  15. lunalib/mining/__pycache__/__init__.cpython-310.pyc +0 -0
  16. lunalib/mining/__pycache__/cuda_manager.cpython-310.pyc +0 -0
  17. lunalib/mining/__pycache__/difficulty.cpython-310.pyc +0 -0
  18. lunalib/mining/__pycache__/miner.cpython-310.pyc +0 -0
  19. lunalib/storage/__pycache__/__init__.cpython-310.pyc +0 -0
  20. lunalib/storage/__pycache__/cache.cpython-310.pyc +0 -0
  21. lunalib/storage/__pycache__/database.cpython-310.pyc +0 -0
  22. lunalib/storage/__pycache__/encryption.cpython-310.pyc +0 -0
  23. lunalib/tests/__pycache__/conftest.cpython-310-pytest-9.0.1.pyc +0 -0
  24. lunalib/tests/__pycache__/test_blockchain.cpython-310-pytest-9.0.1.pyc +0 -0
  25. lunalib/tests/__pycache__/test_crypto.cpython-310-pytest-9.0.1.pyc +0 -0
  26. lunalib/tests/__pycache__/test_gtx.cpython-310-pytest-9.0.1.pyc +0 -0
  27. lunalib/tests/__pycache__/test_mining.cpython-310-pytest-9.0.1.pyc +0 -0
  28. lunalib/tests/__pycache__/test_storage.cpython-310-pytest-9.0.1.pyc +0 -0
  29. lunalib/tests/__pycache__/test_transactions.cpython-310-pytest-9.0.1.pyc +0 -0
  30. lunalib/tests/__pycache__/test_wallet.cpython-310-pytest-9.0.1.pyc +0 -0
  31. lunalib/tests/conftest.py +41 -0
  32. lunalib/tests/init.py +0 -0
  33. lunalib/tests/integration/__pycache__/test_integration.cpython-310-pytest-9.0.1.pyc +0 -0
  34. lunalib/tests/integration/test_integration.py +62 -0
  35. lunalib/tests/test_blockchain.py +34 -0
  36. lunalib/tests/test_crypto.py +42 -0
  37. lunalib/tests/test_gtx.py +135 -0
  38. lunalib/tests/test_mining.py +244 -0
  39. lunalib/tests/test_security_suite.py +832 -0
  40. lunalib/tests/test_storage.py +84 -0
  41. lunalib/tests/test_transactions.py +103 -0
  42. lunalib/tests/test_wallet.py +91 -0
  43. lunalib/transactions/__pycache__/__init__.cpython-310.pyc +0 -0
  44. lunalib/transactions/__pycache__/security.cpython-310.pyc +0 -0
  45. lunalib/transactions/__pycache__/transactions.cpython-310.pyc +0 -0
  46. lunalib/transactions/__pycache__/validator.cpython-310.pyc +0 -0
  47. {lunalib-1.7.3.dist-info → lunalib-1.7.9.dist-info}/METADATA +1 -1
  48. lunalib-1.7.9.dist-info/RECORD +77 -0
  49. lunalib-1.7.3.dist-info/RECORD +0 -34
  50. {lunalib-1.7.3.dist-info → lunalib-1.7.9.dist-info}/WHEEL +0 -0
  51. {lunalib-1.7.3.dist-info → lunalib-1.7.9.dist-info}/top_level.txt +0 -0
lunalib/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .venv
2
+ build
3
+ .__pycache__
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,30 @@ 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
+ # Only count as confirmed if confirmations >= 6
497
+ if confirmations is not None and confirmations >= 6:
498
+ total_balance += amount
446
499
  elif direction == "incoming":
447
500
  total_balance += amount
448
501
  elif direction == "outgoing":
@@ -453,9 +506,9 @@ class LunaWallet:
453
506
  return max(0.0, total_balance)
454
507
 
455
508
  def _compute_pending_totals(
456
- self, pending_txs: List[Dict], address: str
509
+ self, pending_txs: List[Dict], address: str, current_height: int = None
457
510
  ) -> Tuple[float, float]:
458
- """Return (pending_outgoing, pending_incoming) for an address."""
511
+ """Return (pending_outgoing, pending_incoming) for an address, including pending rewards (<6 confs)."""
459
512
  pending_out = 0.0
460
513
  pending_in = 0.0
461
514
 
@@ -463,14 +516,26 @@ class LunaWallet:
463
516
  for tx in pending_txs:
464
517
  from_norm = self._normalize_address(tx.get("from") or tx.get("sender"))
465
518
  to_norm = self._normalize_address(tx.get("to") or tx.get("receiver"))
519
+ tx_type = (tx.get("type") or "").lower()
520
+ amount = float(tx.get("amount", 0) or 0)
521
+ block_height = tx.get("block_height")
522
+ confirmations = None
523
+ if block_height is not None and current_height is not None:
524
+ try:
525
+ confirmations = int(current_height) - int(block_height) + 1
526
+ except Exception:
527
+ confirmations = None
466
528
 
467
529
  if from_norm == target_norm:
468
- amount = float(tx.get("amount", 0) or 0)
469
530
  fee = float(tx.get("fee", 0) or tx.get("gas", 0) or 0)
470
531
  pending_out += amount + fee
471
532
  if to_norm == target_norm:
472
- amount = float(tx.get("amount", 0) or 0)
473
- pending_in += amount
533
+ # For rewards, only count as pending if confirmations < 6
534
+ if tx_type == "reward" or tx.get("from") == "network":
535
+ if confirmations is not None and confirmations < 6:
536
+ pending_in += amount
537
+ else:
538
+ pending_in += amount
474
539
 
475
540
  return pending_out, pending_in
476
541
 
@@ -503,7 +568,7 @@ class LunaWallet:
503
568
  return updated
504
569
 
505
570
  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."""
571
+ """Scan blockchain and mempool for all wallets, update balances and pending info."""
507
572
  addresses = list(self.wallets.keys())
508
573
  if not addresses:
509
574
  return {}
@@ -516,15 +581,44 @@ class LunaWallet:
516
581
  mempool = MempoolManager()
517
582
 
518
583
  confirmed_map = blockchain.scan_transactions_for_addresses(addresses)
519
- pending_map = mempool.get_pending_transactions_for_addresses(
520
- addresses, fetch_remote=True
521
- )
584
+ pending_map = mempool.get_pending_transactions_for_addresses(addresses, fetch_remote=True)
522
585
 
523
586
  # Reset caches with the latest snapshots
524
587
  self._confirmed_tx_cache = confirmed_map
525
588
  self._pending_tx_cache = pending_map
526
589
 
527
- return self._recompute_balances_from_cache()
590
+ # Recompute balances and pending for all wallets
591
+ updated: Dict[str, Dict[str, float]] = {}
592
+ for addr in addresses:
593
+ confirmed = confirmed_map.get(addr, [])
594
+ pending = pending_map.get(addr, [])
595
+ # 最新のブロック高を取得
596
+ try:
597
+ current_height = blockchain.get_blockchain_height()
598
+ except Exception:
599
+ current_height = None
600
+ total_balance = max(0.0, self._compute_confirmed_balance(confirmed))
601
+ pending_out, pending_in = self._compute_pending_totals(pending, addr, current_height)
602
+ available_balance = max(0.0, total_balance - pending_out)
603
+
604
+ wallet_data = self.wallets.get(addr, {})
605
+ wallet_data["balance"] = total_balance
606
+ wallet_data["available_balance"] = available_balance
607
+ wallet_data["pending_out"] = pending_out
608
+ wallet_data["pending_in"] = pending_in
609
+
610
+ if addr == self.current_wallet_address:
611
+ self.balance = total_balance
612
+ self.available_balance = available_balance
613
+
614
+ updated[addr] = {
615
+ "balance": total_balance,
616
+ "available_balance": available_balance,
617
+ "pending_out": pending_out,
618
+ "pending_in": pending_in,
619
+ }
620
+
621
+ return updated
528
622
 
529
623
  except Exception as e:
530
624
  print(f"DEBUG: Error refreshing all wallet balances: {e}")
@@ -543,11 +637,12 @@ class LunaWallet:
543
637
  confirmed = self._confirmed_tx_cache.get(addr, [])
544
638
  pending = self._pending_tx_cache.get(addr, [])
545
639
 
640
+ # pending_in/pending_outはwallet_dataに格納済み
546
641
  overview[addr] = {
547
642
  "balance": wallet_data.get("balance", 0.0),
548
643
  "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],
644
+ "pending_out": wallet_data.get("pending_out", 0.0),
645
+ "pending_in": wallet_data.get("pending_in", 0.0),
551
646
  }
552
647
 
553
648
  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)
@@ -0,0 +1,42 @@
1
+ import pytest
2
+ from lunalib.core.crypto import KeyManager
3
+
4
+ class TestCrypto:
5
+ def test_key_generation(self):
6
+ """Test cryptographic key generation"""
7
+ key_manager = KeyManager()
8
+
9
+ private_key = key_manager.generate_private_key()
10
+ public_key = key_manager.derive_public_key(private_key)
11
+ address = key_manager.derive_address(public_key)
12
+
13
+ assert len(private_key) == 64 # 32 bytes in hex
14
+ assert len(public_key) == 64 # 32 bytes in hex
15
+ assert address.startswith("LUN_")
16
+
17
+ def test_data_signing(self):
18
+ """Test data signing and verification"""
19
+ key_manager = KeyManager()
20
+
21
+ private_key = key_manager.generate_private_key()
22
+ public_key = key_manager.derive_public_key(private_key)
23
+
24
+ test_data = "Hello, Luna Library!"
25
+ signature = key_manager.sign_data(test_data, private_key)
26
+
27
+ # Basic signature format check
28
+ assert len(signature) == 64
29
+ assert all(c in "0123456789abcdef" for c in signature.lower())
30
+
31
+ def test_address_generation_uniqueness(self):
32
+ """Test that addresses are unique"""
33
+ key_manager = KeyManager()
34
+
35
+ addresses = set()
36
+ for _ in range(10):
37
+ private_key = key_manager.generate_private_key()
38
+ public_key = key_manager.derive_public_key(private_key)
39
+ address = key_manager.derive_address(public_key)
40
+ addresses.add(address)
41
+
42
+ assert len(addresses) == 10 # All addresses should be unique