lunalib 1.6.7__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.
- lunalib/.gitignore +3 -0
- lunalib/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/blockchain.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/crypto.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/mempool.cpython-310.pyc +0 -0
- lunalib/core/__pycache__/wallet.cpython-310.pyc +0 -0
- lunalib/core/blockchain.py +182 -2
- lunalib/core/daemon.py +108 -5
- lunalib/core/mempool.py +1 -0
- lunalib/core/wallet.py +856 -492
- lunalib/core/wallet_db.py +68 -0
- lunalib/gtx/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/bill_registry.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/digital_bill.cpython-310.pyc +0 -0
- lunalib/gtx/__pycache__/genesis.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/cuda_manager.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/difficulty.cpython-310.pyc +0 -0
- lunalib/mining/__pycache__/miner.cpython-310.pyc +0 -0
- lunalib/mining/miner.py +33 -14
- lunalib/storage/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/cache.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/database.cpython-310.pyc +0 -0
- lunalib/storage/__pycache__/encryption.cpython-310.pyc +0 -0
- lunalib/tests/__pycache__/conftest.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_blockchain.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_crypto.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_gtx.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_mining.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_storage.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_transactions.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/__pycache__/test_wallet.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/conftest.py +41 -0
- lunalib/tests/init.py +0 -0
- lunalib/tests/integration/__pycache__/test_integration.cpython-310-pytest-9.0.1.pyc +0 -0
- lunalib/tests/integration/test_integration.py +62 -0
- lunalib/tests/test_blockchain.py +34 -0
- lunalib/tests/test_crypto.py +42 -0
- lunalib/tests/test_gtx.py +135 -0
- lunalib/tests/test_mining.py +244 -0
- lunalib/tests/test_security_suite.py +832 -0
- lunalib/tests/test_storage.py +84 -0
- lunalib/tests/test_transactions.py +103 -0
- lunalib/tests/test_wallet.py +91 -0
- lunalib/transactions/__pycache__/__init__.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/security.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/transactions.cpython-310.pyc +0 -0
- lunalib/transactions/__pycache__/validator.cpython-310.pyc +0 -0
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/METADATA +2 -1
- lunalib-1.7.9.dist-info/RECORD +77 -0
- lunalib-1.6.7.dist-info/RECORD +0 -33
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/WHEEL +0 -0
- {lunalib-1.6.7.dist-info → lunalib-1.7.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
from lunalib.storage.database import WalletDatabase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WalletDB:
|
|
6
|
+
def __init__(self, data_dir=None):
|
|
7
|
+
# data_dir is kept for compatibility, but db_path is passed to WalletDatabase
|
|
8
|
+
import os
|
|
9
|
+
self.data_dir = data_dir or os.path.expanduser("~/.lunawallet")
|
|
10
|
+
os.makedirs(self.data_dir, exist_ok=True)
|
|
11
|
+
self.db_path = os.path.join(self.data_dir, "wallets.db")
|
|
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)
|
|
15
|
+
|
|
16
|
+
def save_wallet(self, address, label, public_key, encrypted_private_key, is_locked, created, balance, available_balance):
|
|
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)
|
|
28
|
+
|
|
29
|
+
def load_wallet(self, address):
|
|
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
|
|
37
|
+
|
|
38
|
+
def list_wallets(self):
|
|
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
|
+
|
|
60
|
+
def close(self):
|
|
61
|
+
# No persistent connection to close in WalletDatabase
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# Example usage:
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
db = WalletDB()
|
|
67
|
+
print("Wallets:", db.list_wallets())
|
|
68
|
+
db.close()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
lunalib/mining/miner.py
CHANGED
|
@@ -687,16 +687,20 @@ class Miner:
|
|
|
687
687
|
# Validate all transactions
|
|
688
688
|
valid_transactions = self._validate_transactions(mempool)
|
|
689
689
|
|
|
690
|
-
# If no valid transactions, create a reward transaction
|
|
691
|
-
if not valid_transactions:
|
|
692
|
-
reward_tx = self._create_empty_block_reward(new_index)
|
|
693
|
-
valid_transactions = [reward_tx]
|
|
694
|
-
|
|
695
690
|
# Calculate block difficulty based on transactions
|
|
696
691
|
block_difficulty = self._calculate_block_difficulty(valid_transactions)
|
|
697
692
|
|
|
698
|
-
# Calculate block reward
|
|
699
|
-
|
|
693
|
+
# Calculate block reward:
|
|
694
|
+
# - Empty blocks use LINEAR system: difficulty 1 = 1 LKC, difficulty 2 = 2 LKC, etc.
|
|
695
|
+
# - Blocks with transactions use EXPONENTIAL system: 10^(difficulty-1)
|
|
696
|
+
if not valid_transactions:
|
|
697
|
+
# Empty block: linear reward
|
|
698
|
+
total_reward = float(block_difficulty)
|
|
699
|
+
reward_tx = self._create_empty_block_reward(new_index, block_difficulty, total_reward)
|
|
700
|
+
valid_transactions = [reward_tx]
|
|
701
|
+
else:
|
|
702
|
+
# Regular block: exponential reward
|
|
703
|
+
total_reward = self._calculate_exponential_block_reward(block_difficulty)
|
|
700
704
|
|
|
701
705
|
# Create block data
|
|
702
706
|
block_data = {
|
|
@@ -843,17 +847,19 @@ class Miner:
|
|
|
843
847
|
"""
|
|
844
848
|
return self.difficulty_system.calculate_block_reward(difficulty)
|
|
845
849
|
|
|
846
|
-
def _create_empty_block_reward(self, block_index: int) -> Dict:
|
|
847
|
-
"""Create reward transaction for empty blocks"""
|
|
850
|
+
def _create_empty_block_reward(self, block_index: int, difficulty: int, reward_amount: float) -> Dict:
|
|
851
|
+
"""Create reward transaction for empty blocks using LINEAR reward system (difficulty = reward)"""
|
|
848
852
|
return {
|
|
849
853
|
'type': 'reward',
|
|
850
854
|
'from': 'network',
|
|
851
855
|
'to': self.config.miner_address,
|
|
852
|
-
'amount':
|
|
856
|
+
'amount': reward_amount,
|
|
853
857
|
'timestamp': time.time(),
|
|
854
858
|
'block_height': block_index,
|
|
859
|
+
'difficulty': difficulty,
|
|
855
860
|
'hash': f"reward_{block_index}_{int(time.time())}",
|
|
856
|
-
'description': 'Empty block mining reward'
|
|
861
|
+
'description': f'Empty block mining reward (Linear: Difficulty {difficulty} = {reward_amount} LKC)',
|
|
862
|
+
'is_empty_block': True
|
|
857
863
|
}
|
|
858
864
|
|
|
859
865
|
def _cuda_mine(self, block_data: Dict, difficulty: int) -> Optional[Dict]:
|
|
@@ -1021,14 +1027,27 @@ class Miner:
|
|
|
1021
1027
|
safe_print(f"⚠️ Could not validate previous hash: {e}")
|
|
1022
1028
|
|
|
1023
1029
|
# Validate reward matches difficulty
|
|
1024
|
-
|
|
1030
|
+
# Empty blocks use LINEAR system (difficulty = reward)
|
|
1031
|
+
# Regular blocks use EXPONENTIAL system (10^(difficulty-1))
|
|
1032
|
+
transactions = block.get('transactions', [])
|
|
1033
|
+
is_empty_block = len(transactions) == 1 and transactions[0].get('is_empty_block', False)
|
|
1034
|
+
|
|
1035
|
+
if is_empty_block:
|
|
1036
|
+
# Empty block: linear reward
|
|
1037
|
+
expected_reward = float(difficulty)
|
|
1038
|
+
else:
|
|
1039
|
+
# Regular block: exponential reward
|
|
1040
|
+
expected_reward = self.difficulty_system.calculate_block_reward(difficulty)
|
|
1041
|
+
|
|
1025
1042
|
actual_reward = block.get('reward', 0)
|
|
1026
1043
|
|
|
1027
1044
|
# Allow some tolerance for floating point comparison
|
|
1028
1045
|
if abs(actual_reward - expected_reward) > 0.01:
|
|
1029
|
-
|
|
1046
|
+
reward_type = "Linear" if is_empty_block else "Exponential"
|
|
1047
|
+
return False, f"Reward mismatch ({reward_type}): expected {expected_reward} LKC for difficulty {difficulty}, got {actual_reward} LKC"
|
|
1030
1048
|
|
|
1031
|
-
|
|
1049
|
+
reward_type = "Linear" if is_empty_block else "Exponential"
|
|
1050
|
+
safe_print(f"✅ Block validation passed ({reward_type}): Hash meets difficulty {difficulty}, Reward: {actual_reward} LKC")
|
|
1032
1051
|
return True, ""
|
|
1033
1052
|
|
|
1034
1053
|
def _clear_transactions_from_mempool(self, transactions: List[Dict]):
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
Binary file
|
|
@@ -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
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
def safe_print(*args, **kwargs):
|
|
3
|
+
encoding = sys.stdout.encoding or 'utf-8'
|
|
4
|
+
try:
|
|
5
|
+
print(*args, **kwargs)
|
|
6
|
+
except UnicodeEncodeError:
|
|
7
|
+
print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
|
|
8
|
+
import pytest
|
|
9
|
+
from lunalib.gtx.genesis import GTXGenesis
|
|
10
|
+
from lunalib.gtx.digital_bill import DigitalBill
|
|
11
|
+
import time
|
|
12
|
+
class TestGTXGenesis:
|
|
13
|
+
def test_digital_bill_creation(self, test_gtx):
|
|
14
|
+
"""Test digital bill creation"""
|
|
15
|
+
bill = test_gtx.create_genesis_bill(
|
|
16
|
+
denomination=1000,
|
|
17
|
+
user_address="LUN_test_address_123",
|
|
18
|
+
custom_data={"issuer": "test"}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
assert bill.denomination == 1000
|
|
22
|
+
assert bill.user_address == "LUN_test_address_123"
|
|
23
|
+
assert bill.bill_data["issuer"] == "test"
|
|
24
|
+
assert bill.bill_serial.startswith("GTX1000_")
|
|
25
|
+
|
|
26
|
+
def test_bill_mining_data(self, test_gtx):
|
|
27
|
+
"""Test bill mining data generation"""
|
|
28
|
+
bill = test_gtx.create_genesis_bill(100, "LUN_test")
|
|
29
|
+
mining_data = bill.get_mining_data(nonce=123)
|
|
30
|
+
|
|
31
|
+
assert mining_data["type"] == "GTX_Genesis"
|
|
32
|
+
assert mining_data["denomination"] == 100
|
|
33
|
+
assert mining_data["nonce"] == 123
|
|
34
|
+
assert "previous_hash" in mining_data
|
|
35
|
+
|
|
36
|
+
def test_bill_finalization(self, test_gtx):
|
|
37
|
+
"""Test bill finalization after mining"""
|
|
38
|
+
bill = test_gtx.create_genesis_bill(1000, "LUN_test")
|
|
39
|
+
|
|
40
|
+
final_bill = bill.finalize(
|
|
41
|
+
hash="000abc123",
|
|
42
|
+
nonce=12345,
|
|
43
|
+
mining_time=3.2
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
assert final_bill["success"] is True
|
|
47
|
+
assert final_bill["hash"] == "000abc123"
|
|
48
|
+
assert final_bill["mining_time"] == 3.2
|
|
49
|
+
assert "transaction_data" in final_bill
|
|
50
|
+
|
|
51
|
+
def test_bill_verification(self, test_gtx, temp_dir):
|
|
52
|
+
"""Test bill verification with proper bill data structure"""
|
|
53
|
+
import time
|
|
54
|
+
import hashlib
|
|
55
|
+
from unittest.mock import patch
|
|
56
|
+
|
|
57
|
+
# Create a complete bill_info structure that matches what register_bill expects
|
|
58
|
+
bill_serial = "GTX_TEST_12345"
|
|
59
|
+
timestamp = time.time()
|
|
60
|
+
denomination = 100
|
|
61
|
+
user_address = "LUN_test_verify"
|
|
62
|
+
|
|
63
|
+
# Create metadata that will pass one of the verification methods
|
|
64
|
+
metadata_hash = hashlib.sha256(f"test_metadata_{timestamp}".encode()).hexdigest()
|
|
65
|
+
public_key = f"pub_test_key_{timestamp}"
|
|
66
|
+
signature = metadata_hash # This will pass METHOD 1 verification
|
|
67
|
+
|
|
68
|
+
# Create the complete bill_info structure
|
|
69
|
+
bill_info = {
|
|
70
|
+
'bill_serial': bill_serial,
|
|
71
|
+
'denomination': denomination,
|
|
72
|
+
'user_address': user_address,
|
|
73
|
+
'hash': hashlib.sha256(bill_serial.encode()).hexdigest(),
|
|
74
|
+
'mining_time': 1.5,
|
|
75
|
+
'difficulty': 4,
|
|
76
|
+
'luna_value': denomination,
|
|
77
|
+
'timestamp': timestamp,
|
|
78
|
+
'bill_data': {
|
|
79
|
+
'public_key': public_key,
|
|
80
|
+
'signature': signature,
|
|
81
|
+
'metadata_hash': metadata_hash,
|
|
82
|
+
'issued_to': user_address,
|
|
83
|
+
'front_serial': bill_serial,
|
|
84
|
+
'type': 'GTX_Genesis',
|
|
85
|
+
'back_serial': ''
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Register the bill first
|
|
90
|
+
test_gtx.bill_registry.register_bill(bill_info)
|
|
91
|
+
|
|
92
|
+
# DEBUG: Check what get_bill returns
|
|
93
|
+
bill_data_from_registry = test_gtx.bill_registry.get_bill(bill_serial)
|
|
94
|
+
safe_print(f"DEBUG: Bill data from registry: {bill_data_from_registry}")
|
|
95
|
+
|
|
96
|
+
# Test verification - should pass METHOD 1
|
|
97
|
+
result = test_gtx.verify_bill(bill_serial)
|
|
98
|
+
safe_print(f"DEBUG: Verification result: {result}")
|
|
99
|
+
|
|
100
|
+
assert result["valid"] is True
|
|
101
|
+
|
|
102
|
+
def test_invalid_bill_verification(self, test_gtx):
|
|
103
|
+
"""Test verification of non-existent bill"""
|
|
104
|
+
result = test_gtx.verify_bill("GTX_invalid_serial")
|
|
105
|
+
assert result["valid"] is False
|
|
106
|
+
assert "error" in result
|
|
107
|
+
|
|
108
|
+
def test_portfolio_management(self, test_gtx, temp_dir):
|
|
109
|
+
"""Test user portfolio functionality"""
|
|
110
|
+
# Use a unique user address to avoid conflicts
|
|
111
|
+
unique_user = f"LUN_test_user_{int(time.time())}"
|
|
112
|
+
|
|
113
|
+
# Add some test bills to registry
|
|
114
|
+
for denom in [100, 1000, 10000]:
|
|
115
|
+
bill = test_gtx.create_genesis_bill(denom, unique_user)
|
|
116
|
+
final_bill = bill.finalize(f"000hash{denom}", 123, 1.0)
|
|
117
|
+
|
|
118
|
+
# Register the bill properly
|
|
119
|
+
bill_info = {
|
|
120
|
+
'bill_serial': final_bill['bill_serial'],
|
|
121
|
+
'denomination': denom,
|
|
122
|
+
'user_address': unique_user,
|
|
123
|
+
'hash': f"000hash{denom}",
|
|
124
|
+
'mining_time': 1.0,
|
|
125
|
+
'difficulty': 4,
|
|
126
|
+
'luna_value': denom,
|
|
127
|
+
'timestamp': final_bill['timestamp'],
|
|
128
|
+
'bill_data': final_bill
|
|
129
|
+
}
|
|
130
|
+
test_gtx.bill_registry.register_bill(bill_info)
|
|
131
|
+
|
|
132
|
+
portfolio = test_gtx.get_user_portfolio(unique_user)
|
|
133
|
+
|
|
134
|
+
assert portfolio["user_address"] == unique_user
|
|
135
|
+
assert portfolio["total_bills"] == 3
|