lunalib 1.2.3__py3-none-any.whl → 1.5.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.
- lunalib/core/blockchain.py +315 -150
- lunalib/core/crypto.py +265 -27
- lunalib/core/mempool.py +104 -102
- lunalib/core/sm2.py +723 -0
- lunalib/core/wallet.py +1026 -319
- lunalib/core/wallet_manager.py +638 -0
- lunalib/core/wallet_sync_helper.py +163 -0
- lunalib/mining/miner.py +24 -15
- lunalib/storage/cache.py +13 -4
- lunalib/storage/database.py +14 -5
- lunalib/storage/encryption.py +11 -2
- lunalib/transactions/security.py +96 -25
- lunalib/transactions/transactions.py +256 -431
- lunalib-1.5.2.dist-info/METADATA +20 -0
- lunalib-1.5.2.dist-info/RECORD +31 -0
- lunalib-1.5.2.dist-info/top_level.txt +1 -0
- core/__init__.py +0 -0
- core/blockchain.py +0 -172
- core/crypto.py +0 -32
- core/wallet.py +0 -347
- gtx/__init__.py +0 -0
- gtx/bill_registry.py +0 -122
- gtx/digital_bill.py +0 -273
- gtx/genesis.py +0 -338
- lunalib/requirements.txt +0 -44
- lunalib-1.2.3.dist-info/METADATA +0 -283
- lunalib-1.2.3.dist-info/RECORD +0 -50
- lunalib-1.2.3.dist-info/entry_points.txt +0 -2
- lunalib-1.2.3.dist-info/top_level.txt +0 -6
- mining/__init__.py +0 -0
- mining/cuda_manager.py +0 -137
- mining/difficulty.py +0 -106
- mining/miner.py +0 -107
- storage/__init__.py +0 -0
- storage/cache.py +0 -148
- storage/database.py +0 -222
- storage/encryption.py +0 -105
- transactions/__init__.py +0 -0
- transactions/security.py +0 -172
- transactions/transactions.py +0 -379
- transactions/validator.py +0 -71
- {lunalib-1.2.3.dist-info → lunalib-1.5.2.dist-info}/WHEEL +0 -0
|
@@ -1,276 +1,95 @@
|
|
|
1
1
|
# lunalib/transactions/transactions.py
|
|
2
2
|
import time
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
# --- Unicode-safe print for Windows console ---
|
|
6
|
+
def safe_print(*args, **kwargs):
|
|
7
|
+
try:
|
|
8
|
+
print(*args, **kwargs)
|
|
9
|
+
except UnicodeEncodeError:
|
|
10
|
+
encoding = getattr(sys.stdout, 'encoding', 'utf-8')
|
|
11
|
+
print(*(str(a).encode(encoding, errors='replace').decode(encoding) for a in args), **kwargs)
|
|
3
12
|
import hashlib
|
|
4
13
|
import json
|
|
5
14
|
from typing import Dict, Optional, Tuple, List
|
|
6
15
|
from ..core.mempool import MempoolManager
|
|
7
16
|
|
|
17
|
+
# Import REAL SM2 KeyManager from crypto module
|
|
18
|
+
try:
|
|
19
|
+
from ..core.crypto import KeyManager as SM2KeyManager
|
|
20
|
+
SM2_AVAILABLE = True
|
|
21
|
+
safe_print("DEBUG: Using SM2 KeyManager from crypto module")
|
|
22
|
+
except ImportError as e:
|
|
23
|
+
SM2_AVAILABLE = False
|
|
24
|
+
safe_print(f"WARNING: SM2 KeyManager not available: {e}")
|
|
25
|
+
|
|
8
26
|
class TransactionSecurity:
|
|
9
27
|
"""Transaction security validation and risk assessment"""
|
|
10
28
|
|
|
11
29
|
def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
12
30
|
"""Validate transaction structure"""
|
|
13
|
-
required_fields = ['type', 'from', 'to', 'amount', 'timestamp']
|
|
31
|
+
required_fields = ['type', 'from', 'to', 'amount', 'timestamp', 'hash']
|
|
14
32
|
for field in required_fields:
|
|
15
33
|
if field not in transaction:
|
|
16
34
|
return False, f'Missing required field: {field}'
|
|
17
35
|
|
|
18
36
|
# Validate amount
|
|
19
|
-
if transaction['amount'] <= 0:
|
|
37
|
+
if transaction['amount'] <= 0 and transaction['type'] != 'reward':
|
|
20
38
|
return False, 'Amount must be positive'
|
|
21
39
|
|
|
22
40
|
return True, 'Valid'
|
|
23
41
|
|
|
24
|
-
def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
|
|
25
|
-
"""Enhanced security validation"""
|
|
26
|
-
required_fields = ['type', 'from', 'to', 'amount', 'timestamp', 'hash']
|
|
27
|
-
for field in required_fields:
|
|
28
|
-
if field not in transaction:
|
|
29
|
-
return False, f'Missing required field: {field}'
|
|
30
|
-
|
|
31
|
-
# Validate amount
|
|
32
|
-
if transaction['amount'] <= 0:
|
|
33
|
-
return False, 'Invalid amount'
|
|
34
|
-
|
|
35
|
-
# Validate transaction type
|
|
36
|
-
valid_types = ['transfer', 'stake', 'unstake', 'delegate', 'reward',
|
|
37
|
-
'fee_distribution', 'gtx_genesis', 'validator_join',
|
|
38
|
-
'validator_leave', 'governance_vote']
|
|
39
|
-
if transaction['type'] not in valid_types:
|
|
40
|
-
return False, f'Invalid transaction type: {transaction["type"]}'
|
|
41
|
-
|
|
42
|
-
return True, 'Secure'
|
|
43
|
-
|
|
44
42
|
def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
45
43
|
"""Assess transaction risk level"""
|
|
46
44
|
amount = transaction.get('amount', 0)
|
|
47
45
|
tx_type = transaction.get('type', 'transfer')
|
|
48
46
|
|
|
49
|
-
if tx_type in ['gtx_genesis', 'reward'
|
|
47
|
+
if tx_type in ['gtx_genesis', 'reward']:
|
|
50
48
|
return 'very_low', 'System transaction'
|
|
51
49
|
|
|
52
50
|
if amount > 1000000:
|
|
53
|
-
return 'high', 'Very large transaction
|
|
51
|
+
return 'high', 'Very large transaction'
|
|
54
52
|
elif amount > 100000:
|
|
55
|
-
return '
|
|
56
|
-
elif amount > 10000:
|
|
57
|
-
return 'medium', 'Medium transaction amount'
|
|
58
|
-
elif amount > 1000:
|
|
59
|
-
return 'low', 'Small transaction amount'
|
|
53
|
+
return 'medium', 'Large transaction'
|
|
60
54
|
else:
|
|
61
|
-
return '
|
|
62
|
-
|
|
63
|
-
class KeyManager:
|
|
64
|
-
"""Key management for transaction signing"""
|
|
65
|
-
|
|
66
|
-
def derive_public_key(self, private_key: str) -> str:
|
|
67
|
-
"""Derive public key from private key"""
|
|
68
|
-
if not private_key:
|
|
69
|
-
return "unsigned_public_key"
|
|
70
|
-
return f"pub_{hashlib.sha256(private_key.encode()).hexdigest()[:32]}"
|
|
71
|
-
|
|
72
|
-
def sign_data(self, data: str, private_key: str) -> str:
|
|
73
|
-
"""Sign data with private key"""
|
|
74
|
-
if not private_key:
|
|
75
|
-
return "unsigned_signature"
|
|
76
|
-
return hashlib.sha256(f"{data}{private_key}".encode()).hexdigest()
|
|
77
|
-
|
|
78
|
-
def verify_signature(self, data: str, signature: str, public_key: str) -> bool:
|
|
79
|
-
"""Verify signature (simplified - in production use proper ECDSA)"""
|
|
80
|
-
if signature == "unsigned_signature" or public_key == "unsigned_public_key":
|
|
81
|
-
return True # Allow unsigned system transactions
|
|
82
|
-
|
|
83
|
-
# Simplified verification - in real implementation, use proper cryptographic verification
|
|
84
|
-
expected_signature = hashlib.sha256(f"{data}{public_key}".encode()).hexdigest()
|
|
85
|
-
return signature == expected_signature
|
|
86
|
-
|
|
87
|
-
class FeePoolManager:
|
|
88
|
-
"""Decentralized fee pool management"""
|
|
89
|
-
|
|
90
|
-
def __init__(self):
|
|
91
|
-
self.fee_pool_address = self._generate_fee_pool_address()
|
|
92
|
-
self.pending_fees = 0.0
|
|
93
|
-
self.distribution_blocks = 100 # Distribute fees every 100 blocks
|
|
94
|
-
self.last_distribution_block = 0
|
|
95
|
-
self.collected_fees = [] # Track fee collection history
|
|
96
|
-
|
|
97
|
-
def _generate_fee_pool_address(self) -> str:
|
|
98
|
-
"""Generate deterministic fee pool address"""
|
|
99
|
-
base_data = "LUNA_FEE_POOL_V2"
|
|
100
|
-
return hashlib.sha256(base_data.encode()).hexdigest()[:32]
|
|
101
|
-
|
|
102
|
-
def collect_fee(self, fee_amount: float, transaction_hash: str) -> bool:
|
|
103
|
-
"""Collect fee into the pool"""
|
|
104
|
-
if fee_amount > 0:
|
|
105
|
-
self.pending_fees += fee_amount
|
|
106
|
-
self.collected_fees.append({
|
|
107
|
-
'amount': fee_amount,
|
|
108
|
-
'tx_hash': transaction_hash,
|
|
109
|
-
'timestamp': time.time()
|
|
110
|
-
})
|
|
111
|
-
print(f"DEBUG: Collected fee {fee_amount} from transaction {transaction_hash}")
|
|
112
|
-
return True
|
|
113
|
-
return False
|
|
114
|
-
|
|
115
|
-
def should_distribute(self, current_block_height: int) -> bool:
|
|
116
|
-
"""Check if it's time to distribute fees"""
|
|
117
|
-
return (current_block_height - self.last_distribution_block) >= self.distribution_blocks
|
|
118
|
-
|
|
119
|
-
def calculate_rewards(self, stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
120
|
-
"""Calculate rewards for stakers based on their stake"""
|
|
121
|
-
if total_stake <= 0 or self.pending_fees <= 0:
|
|
122
|
-
return []
|
|
123
|
-
|
|
124
|
-
rewards = []
|
|
125
|
-
for staker in stakers:
|
|
126
|
-
stake_amount = staker.get('stake', 0)
|
|
127
|
-
if stake_amount > 0:
|
|
128
|
-
share = stake_amount / total_stake
|
|
129
|
-
reward = self.pending_fees * share
|
|
130
|
-
rewards.append({
|
|
131
|
-
'address': staker['address'],
|
|
132
|
-
'reward': reward,
|
|
133
|
-
'stake_share': share,
|
|
134
|
-
'stake_amount': stake_amount
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
return rewards
|
|
138
|
-
|
|
139
|
-
def create_distribution_transactions(self, current_block_height: int,
|
|
140
|
-
stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
141
|
-
"""Create fee distribution transactions to stakers"""
|
|
142
|
-
if not self.should_distribute(current_block_height) or self.pending_fees <= 0:
|
|
143
|
-
return []
|
|
144
|
-
|
|
145
|
-
rewards = self.calculate_rewards(stakers, total_stake)
|
|
146
|
-
distribution_txs = []
|
|
147
|
-
|
|
148
|
-
total_distributed = 0
|
|
149
|
-
for reward_info in rewards:
|
|
150
|
-
if reward_info['reward'] > 0:
|
|
151
|
-
distribution_tx = {
|
|
152
|
-
"type": "fee_distribution",
|
|
153
|
-
"from": self.fee_pool_address,
|
|
154
|
-
"to": reward_info['address'],
|
|
155
|
-
"amount": reward_info['reward'],
|
|
156
|
-
"fee": 0.0,
|
|
157
|
-
"block_height": current_block_height,
|
|
158
|
-
"distribution_cycle": current_block_height // self.distribution_blocks,
|
|
159
|
-
"stake_share": reward_info['stake_share'],
|
|
160
|
-
"stake_amount": reward_info['stake_amount'],
|
|
161
|
-
"timestamp": time.time(),
|
|
162
|
-
"hash": self._generate_distribution_hash(reward_info['address'], reward_info['reward'], current_block_height)
|
|
163
|
-
}
|
|
164
|
-
distribution_txs.append(distribution_tx)
|
|
165
|
-
total_distributed += reward_info['reward']
|
|
166
|
-
|
|
167
|
-
# Reset pending fees after distribution
|
|
168
|
-
self.pending_fees = 0.0
|
|
169
|
-
self.last_distribution_block = current_block_height
|
|
170
|
-
|
|
171
|
-
print(f"DEBUG: Distributed {total_distributed} fees to {len(distribution_txs)} stakers")
|
|
172
|
-
return distribution_txs
|
|
173
|
-
|
|
174
|
-
def _generate_distribution_hash(self, address: str, amount: float, block_height: int) -> str:
|
|
175
|
-
"""Generate unique hash for distribution transaction"""
|
|
176
|
-
data = f"fee_dist_{address}_{amount}_{block_height}_{time.time_ns()}"
|
|
177
|
-
return hashlib.sha256(data.encode()).hexdigest()
|
|
178
|
-
|
|
179
|
-
def get_fee_statistics(self) -> Dict:
|
|
180
|
-
"""Get fee pool statistics"""
|
|
181
|
-
total_collected = sum(fee['amount'] for fee in self.collected_fees)
|
|
182
|
-
return {
|
|
183
|
-
'pending_fees': self.pending_fees,
|
|
184
|
-
'total_collected': total_collected,
|
|
185
|
-
'distribution_count': len(self.collected_fees),
|
|
186
|
-
'last_distribution_block': self.last_distribution_block,
|
|
187
|
-
'pool_address': self.fee_pool_address
|
|
188
|
-
}
|
|
55
|
+
return 'low', 'Normal transaction'
|
|
189
56
|
|
|
190
57
|
class FeeCalculator:
|
|
191
|
-
"""
|
|
58
|
+
"""Simple fee calculation"""
|
|
192
59
|
|
|
193
|
-
def __init__(self
|
|
194
|
-
self.fee_pool_manager = fee_pool_manager
|
|
60
|
+
def __init__(self):
|
|
195
61
|
self.fee_config = {
|
|
196
|
-
'transfer': 0.
|
|
197
|
-
'
|
|
198
|
-
'
|
|
199
|
-
'delegate': 0.00005, # Delegation fee
|
|
200
|
-
'validator_join': 0.001, # Validator registration fee
|
|
201
|
-
'validator_leave': 0.0001, # Validator exit fee
|
|
202
|
-
'governance_vote': 0.00001, # Governance voting fee
|
|
203
|
-
'gtx_genesis': 0.0, # No fee for GTX genesis
|
|
204
|
-
'reward': 0.0, # No fee for rewards
|
|
205
|
-
'fee_distribution': 0.0, # No fee for fee distributions
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
# Dynamic fee tiers based on transaction amount
|
|
209
|
-
self.amount_tiers = {
|
|
210
|
-
'micro': (0, 1, 0.000001),
|
|
211
|
-
'small': (1, 100, 0.00001),
|
|
212
|
-
'medium': (100, 10000, 0.0001),
|
|
213
|
-
'large': (10000, 100000, 0.001),
|
|
214
|
-
'xlarge': (100000, float('inf'), 0.01)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
def set_fee(self, transaction_type: str, fee_amount: float):
|
|
218
|
-
"""Set custom fee for a transaction type"""
|
|
219
|
-
self.fee_config[transaction_type] = max(0.0, fee_amount)
|
|
220
|
-
|
|
221
|
-
def get_fee(self, transaction_type: str, amount: float = 0.0) -> float:
|
|
222
|
-
"""Get fee for transaction type and amount"""
|
|
223
|
-
base_fee = self.fee_config.get(transaction_type, 0.00001)
|
|
224
|
-
|
|
225
|
-
# Apply amount-based fee scaling
|
|
226
|
-
for tier_name, (min_amt, max_amt, tier_fee) in self.amount_tiers.items():
|
|
227
|
-
if min_amt <= amount < max_amt:
|
|
228
|
-
return max(base_fee, tier_fee)
|
|
229
|
-
|
|
230
|
-
return base_fee
|
|
231
|
-
|
|
232
|
-
def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
|
|
233
|
-
"""Calculate fee based on transaction size and priority"""
|
|
234
|
-
base_fee_per_byte = 0.0000001 # Base fee per byte
|
|
235
|
-
|
|
236
|
-
priority_multipliers = {
|
|
237
|
-
'low': 0.5,
|
|
238
|
-
'normal': 1.0,
|
|
239
|
-
'high': 2.0,
|
|
240
|
-
'urgent': 5.0
|
|
62
|
+
'transfer': 0.001,
|
|
63
|
+
'reward': 0.0,
|
|
64
|
+
'gtx_genesis': 0.0
|
|
241
65
|
}
|
|
242
|
-
|
|
243
|
-
multiplier = priority_multipliers.get(priority, 1.0)
|
|
244
|
-
return transaction_size * base_fee_per_byte * multiplier
|
|
245
66
|
|
|
246
|
-
def
|
|
247
|
-
"""
|
|
248
|
-
|
|
249
|
-
if fee > 0:
|
|
250
|
-
return self.fee_pool_manager.collect_fee(fee, transaction.get('hash', ''))
|
|
251
|
-
return True
|
|
67
|
+
def get_fee(self, transaction_type: str) -> float:
|
|
68
|
+
"""Get fee for transaction type"""
|
|
69
|
+
return self.fee_config.get(transaction_type, 0.001)
|
|
252
70
|
|
|
253
71
|
class TransactionManager:
|
|
254
72
|
"""Handles transaction creation, signing, validation, and broadcasting"""
|
|
255
73
|
|
|
256
74
|
def __init__(self, network_endpoints: List[str] = None):
|
|
257
75
|
self.security = TransactionSecurity()
|
|
258
|
-
self.
|
|
259
|
-
self.fee_pool_manager = FeePoolManager()
|
|
260
|
-
self.fee_calculator = FeeCalculator(self.fee_pool_manager)
|
|
76
|
+
self.fee_calculator = FeeCalculator()
|
|
261
77
|
self.mempool_manager = MempoolManager(network_endpoints)
|
|
78
|
+
|
|
79
|
+
# Initialize SM2 KeyManager if available
|
|
80
|
+
if SM2_AVAILABLE:
|
|
81
|
+
self.key_manager = SM2KeyManager()
|
|
82
|
+
else:
|
|
83
|
+
safe_print("ERROR: SM2 KeyManager not available - cannot sign transactions")
|
|
84
|
+
self.key_manager = None
|
|
262
85
|
|
|
263
86
|
def create_transaction(self, from_address: str, to_address: str, amount: float,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
"""Create and sign a transaction with configurable fees"""
|
|
87
|
+
private_key: Optional[str] = None, memo: str = "",
|
|
88
|
+
transaction_type: str = "transfer") -> Dict:
|
|
89
|
+
"""Create and sign a transaction"""
|
|
268
90
|
|
|
269
91
|
# Calculate fee
|
|
270
|
-
|
|
271
|
-
fee = max(0.0, fee_override)
|
|
272
|
-
else:
|
|
273
|
-
fee = self.fee_calculator.get_fee(transaction_type, amount)
|
|
92
|
+
fee = self.fee_calculator.get_fee(transaction_type)
|
|
274
93
|
|
|
275
94
|
transaction = {
|
|
276
95
|
"type": transaction_type,
|
|
@@ -278,29 +97,56 @@ class TransactionManager:
|
|
|
278
97
|
"to": to_address,
|
|
279
98
|
"amount": float(amount),
|
|
280
99
|
"fee": fee,
|
|
281
|
-
"
|
|
282
|
-
"timestamp": time.time(),
|
|
100
|
+
"timestamp": int(time.time()),
|
|
283
101
|
"memo": memo,
|
|
284
|
-
"
|
|
285
|
-
"version": "1.0"
|
|
102
|
+
"version": "2.0" # Version 2.0 = SM2 signatures
|
|
286
103
|
}
|
|
287
104
|
|
|
288
|
-
#
|
|
289
|
-
if private_key:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
105
|
+
# Sign transaction with SM2 if private key provided
|
|
106
|
+
if private_key and self.key_manager:
|
|
107
|
+
try:
|
|
108
|
+
# Sign the transaction data
|
|
109
|
+
tx_string = self._get_signing_data(transaction)
|
|
110
|
+
|
|
111
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Signing data: {tx_string}")
|
|
112
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Private key available: {bool(private_key)}")
|
|
113
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Private key length: {len(private_key)}")
|
|
114
|
+
|
|
115
|
+
signature = self.key_manager.sign_data(tx_string, private_key)
|
|
116
|
+
|
|
117
|
+
# Get public key from private key
|
|
118
|
+
public_key = self.key_manager.derive_public_key(private_key)
|
|
119
|
+
|
|
120
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Generated signature: {signature}")
|
|
121
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Generated public key: {public_key}")
|
|
122
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Signature length: {len(signature)}")
|
|
123
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Public key length: {len(public_key)}")
|
|
124
|
+
|
|
125
|
+
transaction["signature"] = signature
|
|
126
|
+
transaction["public_key"] = public_key
|
|
127
|
+
|
|
128
|
+
# Immediately test verification
|
|
129
|
+
test_verify = self.key_manager.verify_signature(tx_string, signature, public_key)
|
|
130
|
+
safe_print(f"[TRANSACTIONS CREATE DEBUG] Immediate self-verification: {test_verify}")
|
|
131
|
+
|
|
132
|
+
if not test_verify:
|
|
133
|
+
safe_print(f"[TRANSACTIONS CREATE ERROR] Signature doesn't verify immediately!")
|
|
134
|
+
safe_print(f"[TRANSACTIONS CREATE ERROR] This suggests an SM2 implementation issue")
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
safe_print(f"[TRANSACTIONS CREATE ERROR] Signing failed: {e}")
|
|
138
|
+
import traceback
|
|
139
|
+
traceback.print_exc()
|
|
140
|
+
transaction["signature"] = "unsigned"
|
|
141
|
+
transaction["public_key"] = "unsigned"
|
|
294
142
|
else:
|
|
295
|
-
# For unsigned transactions (
|
|
296
|
-
transaction["public_key"] = "unsigned"
|
|
143
|
+
# For unsigned transactions (rewards, gtx_genesis)
|
|
297
144
|
transaction["signature"] = "unsigned"
|
|
145
|
+
transaction["public_key"] = "unsigned"
|
|
298
146
|
|
|
147
|
+
# Calculate transaction hash (must be last)
|
|
299
148
|
transaction["hash"] = self._calculate_transaction_hash(transaction)
|
|
300
149
|
|
|
301
|
-
# Automatically collect fee
|
|
302
|
-
self.fee_calculator.process_transaction_fee(transaction)
|
|
303
|
-
|
|
304
150
|
return transaction
|
|
305
151
|
|
|
306
152
|
def send_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
@@ -311,19 +157,24 @@ class TransactionManager:
|
|
|
311
157
|
if not is_valid:
|
|
312
158
|
return False, f"Validation failed: {message}"
|
|
313
159
|
|
|
160
|
+
# Verify signature for non-system transactions
|
|
161
|
+
if transaction["type"] == "transfer":
|
|
162
|
+
if not self.verify_transaction_signature(transaction):
|
|
163
|
+
return False, "Invalid transaction signature"
|
|
164
|
+
|
|
314
165
|
# Add to mempool
|
|
315
166
|
success = self.mempool_manager.add_transaction(transaction)
|
|
316
167
|
if success:
|
|
317
|
-
return True, f"Transaction added to mempool: {transaction.get('hash')}"
|
|
168
|
+
return True, f"Transaction added to mempool: {transaction.get('hash', '')[:16]}..."
|
|
318
169
|
else:
|
|
319
170
|
return False, "Failed to add transaction to mempool"
|
|
320
171
|
|
|
321
172
|
except Exception as e:
|
|
322
173
|
return False, f"Error sending transaction: {str(e)}"
|
|
323
174
|
|
|
324
|
-
# TRANSFER
|
|
175
|
+
# TRANSFER TRANSACTION
|
|
325
176
|
def create_transfer(self, from_address: str, to_address: str, amount: float,
|
|
326
|
-
private_key: str, memo: str = ""
|
|
177
|
+
private_key: str, memo: str = "") -> Dict:
|
|
327
178
|
"""Create a transfer transaction"""
|
|
328
179
|
return self.create_transaction(
|
|
329
180
|
from_address=from_address,
|
|
@@ -331,109 +182,30 @@ class TransactionManager:
|
|
|
331
182
|
amount=amount,
|
|
332
183
|
private_key=private_key,
|
|
333
184
|
memo=memo,
|
|
334
|
-
transaction_type="transfer"
|
|
335
|
-
fee_override=fee_override
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
# STAKING TRANSACTIONS
|
|
339
|
-
def create_stake(self, from_address: str, amount: float, private_key: str) -> Dict:
|
|
340
|
-
"""Create staking transaction"""
|
|
341
|
-
return self.create_transaction(
|
|
342
|
-
from_address=from_address,
|
|
343
|
-
to_address="staking_pool",
|
|
344
|
-
amount=amount,
|
|
345
|
-
private_key=private_key,
|
|
346
|
-
transaction_type="stake"
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
def create_unstake(self, from_address: str, amount: float, private_key: str) -> Dict:
|
|
350
|
-
"""Create unstaking transaction"""
|
|
351
|
-
return self.create_transaction(
|
|
352
|
-
from_address="staking_pool",
|
|
353
|
-
to_address=from_address,
|
|
354
|
-
amount=amount,
|
|
355
|
-
private_key=private_key,
|
|
356
|
-
transaction_type="unstake"
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
def create_delegate(self, from_address: str, to_validator: str, amount: float,
|
|
360
|
-
private_key: str) -> Dict:
|
|
361
|
-
"""Create delegation transaction"""
|
|
362
|
-
return self.create_transaction(
|
|
363
|
-
from_address=from_address,
|
|
364
|
-
to_address=to_validator,
|
|
365
|
-
amount=amount,
|
|
366
|
-
private_key=private_key,
|
|
367
|
-
transaction_type="delegate"
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
# VALIDATOR TRANSACTIONS
|
|
371
|
-
def create_validator_join(self, from_address: str, validator_info: Dict,
|
|
372
|
-
private_key: str) -> Dict:
|
|
373
|
-
"""Create validator registration transaction"""
|
|
374
|
-
transaction = self.create_transaction(
|
|
375
|
-
from_address=from_address,
|
|
376
|
-
to_address="validator_registry",
|
|
377
|
-
amount=validator_info.get('stake', 0),
|
|
378
|
-
private_key=private_key,
|
|
379
|
-
transaction_type="validator_join"
|
|
380
|
-
)
|
|
381
|
-
# Add validator-specific info
|
|
382
|
-
transaction.update({
|
|
383
|
-
"validator_name": validator_info.get('name', ''),
|
|
384
|
-
"validator_url": validator_info.get('url', ''),
|
|
385
|
-
"commission_rate": validator_info.get('commission_rate', 0.0)
|
|
386
|
-
})
|
|
387
|
-
return transaction
|
|
388
|
-
|
|
389
|
-
def create_validator_leave(self, from_address: str, private_key: str) -> Dict:
|
|
390
|
-
"""Create validator exit transaction"""
|
|
391
|
-
return self.create_transaction(
|
|
392
|
-
from_address=from_address,
|
|
393
|
-
to_address="validator_exit",
|
|
394
|
-
amount=0, # No amount for exit
|
|
395
|
-
private_key=private_key,
|
|
396
|
-
transaction_type="validator_leave"
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
# GOVERNANCE TRANSACTIONS
|
|
400
|
-
def create_governance_vote(self, from_address: str, proposal_id: str,
|
|
401
|
-
vote: str, private_key: str) -> Dict:
|
|
402
|
-
"""Create governance vote transaction"""
|
|
403
|
-
transaction = self.create_transaction(
|
|
404
|
-
from_address=from_address,
|
|
405
|
-
to_address="governance",
|
|
406
|
-
amount=0, # No amount for voting
|
|
407
|
-
private_key=private_key,
|
|
408
|
-
transaction_type="governance_vote"
|
|
185
|
+
transaction_type="transfer"
|
|
409
186
|
)
|
|
410
|
-
# Add governance-specific info
|
|
411
|
-
transaction.update({
|
|
412
|
-
"proposal_id": proposal_id,
|
|
413
|
-
"vote": vote, # 'yes', 'no', 'abstain'
|
|
414
|
-
"voting_power": 1.0 # Could be based on stake
|
|
415
|
-
})
|
|
416
|
-
return transaction
|
|
417
187
|
|
|
418
|
-
# SYSTEM TRANSACTIONS
|
|
188
|
+
# SYSTEM TRANSACTIONS (unsigned)
|
|
419
189
|
def create_gtx_transaction(self, bill_info: Dict) -> Dict:
|
|
420
190
|
"""Create GTX Genesis transaction from mined bill"""
|
|
421
|
-
|
|
191
|
+
transaction = {
|
|
422
192
|
"type": "gtx_genesis",
|
|
423
193
|
"from": "mining",
|
|
424
194
|
"to": bill_info.get("owner_address", "unknown"),
|
|
425
|
-
"amount": bill_info.get("denomination", 0),
|
|
195
|
+
"amount": float(bill_info.get("denomination", 0)),
|
|
426
196
|
"fee": 0.0,
|
|
427
|
-
"timestamp": time.time(),
|
|
197
|
+
"timestamp": int(time.time()),
|
|
428
198
|
"bill_serial": bill_info.get("serial", ""),
|
|
429
199
|
"mining_difficulty": bill_info.get("difficulty", 0),
|
|
430
|
-
"
|
|
200
|
+
"signature": "system",
|
|
431
201
|
"public_key": "system",
|
|
432
|
-
"
|
|
202
|
+
"version": "2.0"
|
|
433
203
|
}
|
|
204
|
+
transaction["hash"] = self._calculate_transaction_hash(transaction)
|
|
205
|
+
return transaction
|
|
434
206
|
|
|
435
207
|
def create_reward_transaction(self, to_address: str, amount: float,
|
|
436
|
-
block_height: int
|
|
208
|
+
block_height: int) -> Dict:
|
|
437
209
|
"""Create reward transaction"""
|
|
438
210
|
transaction = {
|
|
439
211
|
"type": "reward",
|
|
@@ -442,61 +214,122 @@ class TransactionManager:
|
|
|
442
214
|
"amount": float(amount),
|
|
443
215
|
"fee": 0.0,
|
|
444
216
|
"block_height": block_height,
|
|
445
|
-
"
|
|
446
|
-
"
|
|
447
|
-
"hash": self._generate_reward_hash(to_address, amount, block_height, reward_type),
|
|
217
|
+
"timestamp": int(time.time()),
|
|
218
|
+
"signature": "system",
|
|
448
219
|
"public_key": "system",
|
|
449
|
-
"
|
|
220
|
+
"version": "2.0"
|
|
450
221
|
}
|
|
222
|
+
transaction["hash"] = self._generate_reward_hash(to_address, amount, block_height)
|
|
451
223
|
return transaction
|
|
452
224
|
|
|
453
|
-
def create_fee_distribution(self, to_address: str, amount: float,
|
|
454
|
-
distribution_cycle: int, stake_share: float) -> Dict:
|
|
455
|
-
"""Create fee distribution transaction"""
|
|
456
|
-
return {
|
|
457
|
-
"type": "fee_distribution",
|
|
458
|
-
"from": self.fee_pool_manager.fee_pool_address,
|
|
459
|
-
"to": to_address,
|
|
460
|
-
"amount": float(amount),
|
|
461
|
-
"fee": 0.0,
|
|
462
|
-
"distribution_cycle": distribution_cycle,
|
|
463
|
-
"stake_share": stake_share,
|
|
464
|
-
"timestamp": time.time(),
|
|
465
|
-
"hash": self.fee_pool_manager._generate_distribution_hash(to_address, amount, distribution_cycle),
|
|
466
|
-
"public_key": "system",
|
|
467
|
-
"signature": "system"
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
# FEE DISTRIBUTION
|
|
471
|
-
def distribute_fees(self, current_block_height: int, stakers: List[Dict],
|
|
472
|
-
total_stake: float) -> List[Dict]:
|
|
473
|
-
"""Distribute collected fees to stakers"""
|
|
474
|
-
return self.fee_pool_manager.create_distribution_transactions(
|
|
475
|
-
current_block_height, stakers, total_stake
|
|
476
|
-
)
|
|
477
|
-
|
|
478
225
|
# VALIDATION METHODS
|
|
479
226
|
def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
480
227
|
"""Validate transaction using security module"""
|
|
481
228
|
return self.security.validate_transaction(transaction)
|
|
482
229
|
|
|
483
|
-
def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
|
|
484
|
-
"""Validate transaction security"""
|
|
485
|
-
return self.security.validate_transaction_security(transaction)
|
|
486
|
-
|
|
487
|
-
def assess_transaction_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
488
|
-
"""Assess transaction risk level"""
|
|
489
|
-
return self.security.assess_risk(transaction)
|
|
490
|
-
|
|
491
230
|
def verify_transaction_signature(self, transaction: Dict) -> bool:
|
|
492
|
-
"""Verify transaction signature"""
|
|
231
|
+
"""Verify transaction signature with SM2"""
|
|
493
232
|
try:
|
|
494
|
-
sign_data = self._get_signing_data(transaction)
|
|
495
233
|
signature = transaction.get("signature", "")
|
|
234
|
+
tx_type = transaction.get("type", "").lower()
|
|
235
|
+
|
|
236
|
+
# System transactions are always valid
|
|
237
|
+
if signature in ["system", "unsigned", "test"]:
|
|
238
|
+
print(f"[TRANSACTIONS] Skipping signature check for {signature} transaction")
|
|
239
|
+
return True
|
|
240
|
+
|
|
241
|
+
if not self.key_manager:
|
|
242
|
+
print("[TRANSACTIONS] No key manager available for verification")
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
# Check SM2 signature format
|
|
246
|
+
if len(signature) != 128:
|
|
247
|
+
print(f"[TRANSACTIONS] Invalid SM2 signature length: {len(signature)} (expected 128)")
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
# Get signing data (without public_key!)
|
|
251
|
+
sign_data = self._get_signing_data(transaction)
|
|
496
252
|
public_key = transaction.get("public_key", "")
|
|
497
|
-
|
|
498
|
-
|
|
253
|
+
|
|
254
|
+
print(f"[TRANSACTIONS VERIFY] Signing data length: {len(sign_data)}")
|
|
255
|
+
print(f"[TRANSACTIONS VERIFY] Signing data (first 100 chars): {sign_data[:100]}")
|
|
256
|
+
|
|
257
|
+
# Try to verify with KeyManager
|
|
258
|
+
print(f"[TRANSACTIONS] Attempting verification...")
|
|
259
|
+
is_valid = self.key_manager.verify_signature(sign_data, signature, public_key)
|
|
260
|
+
|
|
261
|
+
print(f"[TRANSACTIONS] SM2 signature verification result: {is_valid}")
|
|
262
|
+
|
|
263
|
+
return is_valid
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
print(f"[TRANSACTIONS] Verification error: {e}")
|
|
267
|
+
import traceback
|
|
268
|
+
traceback.print_exc()
|
|
499
269
|
return False
|
|
270
|
+
def _debug_signature_issue(self, transaction: Dict, sign_data: str, signature: str, public_key: str):
|
|
271
|
+
"""Debug why signature verification is failing"""
|
|
272
|
+
print("\n" + "="*60)
|
|
273
|
+
print("DEBUGGING SIGNATURE ISSUE")
|
|
274
|
+
print("="*60)
|
|
275
|
+
|
|
276
|
+
# 1. Check if we can sign and verify a simple test
|
|
277
|
+
print("\n1. Testing SM2 with simple message...")
|
|
278
|
+
test_message = "Simple test message"
|
|
279
|
+
test_private = self.key_manager.generate_private_key()
|
|
280
|
+
test_public = self.key_manager.derive_public_key(test_private)
|
|
281
|
+
test_sig = self.key_manager.sign_data(test_message, test_private)
|
|
282
|
+
test_valid = self.key_manager.verify_signature(test_message, test_sig, test_public)
|
|
283
|
+
print(f" Simple test verification: {test_valid}")
|
|
284
|
+
|
|
285
|
+
# 2. Try to recreate what was signed during transaction creation
|
|
286
|
+
print("\n2. Reconstructing original transaction data...")
|
|
287
|
+
# Create the exact transaction data that should have been signed
|
|
288
|
+
reconstructed = {
|
|
289
|
+
"amount": float(transaction["amount"]),
|
|
290
|
+
"fee": float(transaction["fee"]),
|
|
291
|
+
"from": transaction["from"],
|
|
292
|
+
"memo": transaction.get("memo", ""),
|
|
293
|
+
"timestamp": int(transaction["timestamp"]),
|
|
294
|
+
"to": transaction["to"],
|
|
295
|
+
"type": transaction["type"],
|
|
296
|
+
"version": transaction.get("version", "2.0")
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
import json
|
|
300
|
+
reconstructed_json = json.dumps(reconstructed, sort_keys=True)
|
|
301
|
+
print(f" Reconstructed JSON: {reconstructed_json}")
|
|
302
|
+
print(f" Current signing data: {sign_data}")
|
|
303
|
+
print(f" Are they equal? {reconstructed_json == sign_data}")
|
|
304
|
+
print(f" Length difference: {len(reconstructed_json)} vs {len(sign_data)}")
|
|
305
|
+
|
|
306
|
+
# 3. Check for whitespace differences
|
|
307
|
+
print("\n3. Checking for whitespace differences...")
|
|
308
|
+
print(f" Reconstructed has spaces: {' ' in reconstructed_json}")
|
|
309
|
+
print(f" Sign data has spaces: {' ' in sign_data}")
|
|
310
|
+
|
|
311
|
+
# 4. Check float formatting
|
|
312
|
+
print("\n4. Checking float formatting...")
|
|
313
|
+
print(f" Amount in tx: {transaction['amount']} (type: {type(transaction['amount'])})")
|
|
314
|
+
print(f" Amount in reconstructed: {reconstructed['amount']} (type: {type(reconstructed['amount'])})")
|
|
315
|
+
|
|
316
|
+
# 5. Try different JSON serialization options
|
|
317
|
+
print("\n5. Trying different JSON formats...")
|
|
318
|
+
formats = [
|
|
319
|
+
("Compact", lambda x: json.dumps(x, sort_keys=True, separators=(',', ':'))),
|
|
320
|
+
("Default", lambda x: json.dumps(x, sort_keys=True)),
|
|
321
|
+
("Indented", lambda x: json.dumps(x, sort_keys=True, indent=2)),
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
for name, formatter in formats:
|
|
325
|
+
formatted = formatter(reconstructed)
|
|
326
|
+
is_valid_test = self.key_manager.verify_signature(formatted, signature, public_key)
|
|
327
|
+
print(f" {name} format: {is_valid_test} (length: {len(formatted)})")
|
|
328
|
+
|
|
329
|
+
print("="*60 + "\n")
|
|
330
|
+
def assess_transaction_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
331
|
+
"""Assess transaction risk level"""
|
|
332
|
+
return self.security.assess_risk(transaction)
|
|
500
333
|
|
|
501
334
|
# MEMPOOL MANAGEMENT
|
|
502
335
|
def get_pending_transactions(self, address: str = None) -> List[Dict]:
|
|
@@ -507,77 +340,69 @@ class TransactionManager:
|
|
|
507
340
|
"""Check if transaction is pending in mempool"""
|
|
508
341
|
return self.mempool_manager.is_transaction_pending(tx_hash)
|
|
509
342
|
|
|
510
|
-
def is_transaction_confirmed(self, tx_hash: str) -> bool:
|
|
511
|
-
"""Check if transaction has been confirmed"""
|
|
512
|
-
return self.mempool_manager.is_transaction_confirmed(tx_hash)
|
|
513
|
-
|
|
514
|
-
# FEE MANAGEMENT
|
|
515
|
-
def set_transaction_fee(self, transaction_type: str, fee_amount: float):
|
|
516
|
-
"""Set custom fee for transaction type"""
|
|
517
|
-
self.fee_calculator.set_fee(transaction_type, fee_amount)
|
|
518
|
-
|
|
519
|
-
def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
|
|
520
|
-
"""Calculate network fee based on size and priority"""
|
|
521
|
-
return self.fee_calculator.calculate_network_fee(transaction_size, priority)
|
|
522
|
-
|
|
523
|
-
def get_fee_pool_balance(self) -> float:
|
|
524
|
-
"""Get current fee pool balance"""
|
|
525
|
-
return self.fee_pool_manager.pending_fees
|
|
526
|
-
|
|
527
|
-
def get_fee_pool_address(self) -> str:
|
|
528
|
-
"""Get fee pool address"""
|
|
529
|
-
return self.fee_pool_manager.fee_pool_address
|
|
530
|
-
|
|
531
|
-
def get_fee_statistics(self) -> Dict:
|
|
532
|
-
"""Get fee pool statistics"""
|
|
533
|
-
return self.fee_pool_manager.get_fee_statistics()
|
|
534
|
-
|
|
535
|
-
# UTILITY METHODS
|
|
536
343
|
def _get_signing_data(self, transaction: Dict) -> str:
|
|
537
|
-
"""Create data string for signing"""
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
344
|
+
"""Create data string for signing - MUST match signing process exactly"""
|
|
345
|
+
# IMPORTANT: During signing, the transaction does NOT have 'public_key' yet!
|
|
346
|
+
# It's added AFTER signing. So we must exclude it during verification too.
|
|
347
|
+
|
|
348
|
+
# Fields that should NOT be included in signing data:
|
|
349
|
+
exclude_fields = ['signature', 'hash', 'public_key']
|
|
350
|
+
|
|
351
|
+
# Create copy without excluded fields
|
|
352
|
+
tx_copy = {k: v for k, v in transaction.items()
|
|
353
|
+
if k not in exclude_fields}
|
|
354
|
+
|
|
355
|
+
# Use the EXACT same format as during creation
|
|
356
|
+
import json
|
|
357
|
+
|
|
358
|
+
# Ensure consistent data types
|
|
359
|
+
for key in ['amount', 'fee']:
|
|
360
|
+
if key in tx_copy:
|
|
361
|
+
tx_copy[key] = float(tx_copy[key])
|
|
362
|
+
|
|
363
|
+
if 'timestamp' in tx_copy:
|
|
364
|
+
tx_copy['timestamp'] = int(tx_copy['timestamp'])
|
|
365
|
+
|
|
366
|
+
# Use COMPACT JSON (no extra spaces) - this is critical!
|
|
367
|
+
return json.dumps(tx_copy, sort_keys=True, separators=(',', ':'))
|
|
551
368
|
def _calculate_transaction_hash(self, transaction: Dict) -> str:
|
|
552
369
|
"""Calculate transaction hash"""
|
|
553
|
-
#
|
|
370
|
+
# Remove existing hash if present
|
|
554
371
|
tx_copy = transaction.copy()
|
|
555
|
-
tx_copy.pop("signature", None)
|
|
556
372
|
tx_copy.pop("hash", None)
|
|
373
|
+
|
|
374
|
+
# Convert to JSON and hash
|
|
557
375
|
data_string = json.dumps(tx_copy, sort_keys=True)
|
|
558
376
|
return hashlib.sha256(data_string.encode()).hexdigest()
|
|
559
377
|
|
|
560
378
|
def _generate_reward_hash(self, to_address: str, amount: float,
|
|
561
|
-
block_height: int
|
|
379
|
+
block_height: int) -> str:
|
|
562
380
|
"""Generate unique hash for reward transaction"""
|
|
563
|
-
data = f"reward_{
|
|
381
|
+
data = f"reward_{to_address}_{amount}_{block_height}_{time.time_ns()}"
|
|
564
382
|
return hashlib.sha256(data.encode()).hexdigest()
|
|
565
383
|
|
|
566
384
|
def stop(self):
|
|
567
385
|
"""Stop the transaction manager"""
|
|
568
386
|
self.mempool_manager.stop()
|
|
569
387
|
|
|
570
|
-
#
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
388
|
+
# Create a global instance for convenience
|
|
389
|
+
_transaction_manager = None
|
|
390
|
+
|
|
391
|
+
def get_transaction_manager() -> TransactionManager:
|
|
392
|
+
"""Get or create global transaction manager instance"""
|
|
393
|
+
global _transaction_manager
|
|
394
|
+
if _transaction_manager is None:
|
|
395
|
+
_transaction_manager = TransactionManager()
|
|
396
|
+
return _transaction_manager
|
|
397
|
+
|
|
398
|
+
# Convenience functions
|
|
399
|
+
def create_transfer(from_address: str, to_address: str, amount: float,
|
|
400
|
+
private_key: str, memo: str = "") -> Dict:
|
|
401
|
+
"""Create transfer transaction (convenience function)"""
|
|
402
|
+
mgr = get_transaction_manager()
|
|
403
|
+
return mgr.create_transfer(from_address, to_address, amount, private_key, memo)
|
|
404
|
+
|
|
405
|
+
def send_transaction(transaction: Dict) -> Tuple[bool, str]:
|
|
406
|
+
"""Send transaction (convenience function)"""
|
|
407
|
+
mgr = get_transaction_manager()
|
|
408
|
+
return mgr.send_transaction(transaction)
|