lunalib 1.2.3__py3-none-any.whl → 1.5.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.
- core/wallet.py +407 -346
- 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 +850 -319
- lunalib/transactions/security.py +87 -25
- lunalib/transactions/transactions.py +247 -431
- {lunalib-1.2.3.dist-info → lunalib-1.5.0.dist-info}/METADATA +1 -1
- {lunalib-1.2.3.dist-info → lunalib-1.5.0.dist-info}/RECORD +14 -13
- transactions/transactions.py +423 -378
- {lunalib-1.2.3.dist-info → lunalib-1.5.0.dist-info}/WHEEL +0 -0
- {lunalib-1.2.3.dist-info → lunalib-1.5.0.dist-info}/entry_points.txt +0 -0
- {lunalib-1.2.3.dist-info → lunalib-1.5.0.dist-info}/top_level.txt +0 -0
transactions/transactions.py
CHANGED
|
@@ -1,379 +1,424 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import hashlib
|
|
3
|
-
import json
|
|
4
|
-
from typing import Dict, Optional, Tuple, List
|
|
5
|
-
|
|
6
|
-
class TransactionSecurity:
|
|
7
|
-
"""Transaction security validation and risk assessment"""
|
|
8
|
-
|
|
9
|
-
def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
10
|
-
"""Validate transaction structure"""
|
|
11
|
-
required_fields = ['from', 'to', 'amount', 'timestamp']
|
|
12
|
-
for field in required_fields:
|
|
13
|
-
if field not in transaction:
|
|
14
|
-
return False, f'Missing required field: {field}'
|
|
15
|
-
|
|
16
|
-
# Validate amount
|
|
17
|
-
if transaction['amount'] <= 0:
|
|
18
|
-
return False, 'Amount must be positive'
|
|
19
|
-
|
|
20
|
-
return True, 'Valid'
|
|
21
|
-
|
|
22
|
-
def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
|
|
23
|
-
"""Enhanced security validation"""
|
|
24
|
-
required_fields = ['from', 'to', 'amount', 'timestamp', 'type']
|
|
25
|
-
for field in required_fields:
|
|
26
|
-
if field not in transaction:
|
|
27
|
-
return False, f'Missing required field: {field}'
|
|
28
|
-
|
|
29
|
-
# Validate amount
|
|
30
|
-
if transaction['amount'] <= 0:
|
|
31
|
-
return False, 'Invalid amount'
|
|
32
|
-
|
|
33
|
-
return True, 'Secure'
|
|
34
|
-
|
|
35
|
-
def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
36
|
-
"""Assess transaction risk level"""
|
|
37
|
-
amount = transaction.get('amount', 0)
|
|
38
|
-
|
|
39
|
-
if amount > 100000:
|
|
40
|
-
return 'high', 'Large transaction amount'
|
|
41
|
-
elif amount > 10000:
|
|
42
|
-
return 'medium', 'Medium transaction amount'
|
|
43
|
-
else:
|
|
44
|
-
return 'low', 'Normal transaction'
|
|
45
|
-
|
|
46
|
-
class KeyManager:
|
|
47
|
-
"""Key management for transaction signing"""
|
|
48
|
-
|
|
49
|
-
def derive_public_key(self, private_key: str) -> str:
|
|
50
|
-
"""Derive public key from private key"""
|
|
51
|
-
if not private_key:
|
|
52
|
-
return "default_public_key"
|
|
53
|
-
return f"pub_{private_key[-16:]}"
|
|
54
|
-
|
|
55
|
-
def sign_data(self, data: str, private_key: str) -> str:
|
|
56
|
-
"""Sign data with private key"""
|
|
57
|
-
if not private_key:
|
|
58
|
-
return "default_signature"
|
|
59
|
-
return hashlib.sha256(f"{data}{private_key}".encode()).hexdigest()
|
|
60
|
-
|
|
61
|
-
class FeePoolManager:
|
|
62
|
-
"""Decentralized fee pool management"""
|
|
63
|
-
|
|
64
|
-
def __init__(self):
|
|
65
|
-
self.fee_pool_address = self._generate_fee_pool_address()
|
|
66
|
-
self.pending_fees = 0.0
|
|
67
|
-
self.distribution_blocks = 100 # Distribute fees every 100 blocks
|
|
68
|
-
self.last_distribution_block = 0
|
|
69
|
-
|
|
70
|
-
def _generate_fee_pool_address(self) -> str:
|
|
71
|
-
"""Generate deterministic fee pool address"""
|
|
72
|
-
base_data = "LUNA_FEE_POOL_V1"
|
|
73
|
-
return hashlib.sha256(base_data.encode()).hexdigest()[:32]
|
|
74
|
-
|
|
75
|
-
def collect_fee(self, fee_amount: float, transaction_hash: str) -> bool:
|
|
76
|
-
"""Collect fee into the pool"""
|
|
77
|
-
if fee_amount > 0:
|
|
78
|
-
self.pending_fees += fee_amount
|
|
79
|
-
return True
|
|
80
|
-
return False
|
|
81
|
-
|
|
82
|
-
def should_distribute(self, current_block_height: int) -> bool:
|
|
83
|
-
"""Check if it's time to distribute fees"""
|
|
84
|
-
return (current_block_height - self.last_distribution_block) >= self.distribution_blocks
|
|
85
|
-
|
|
86
|
-
def calculate_rewards(self, stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
87
|
-
"""Calculate rewards for stakers based on their stake"""
|
|
88
|
-
if total_stake <= 0 or self.pending_fees <= 0:
|
|
89
|
-
return []
|
|
90
|
-
|
|
91
|
-
rewards = []
|
|
92
|
-
for staker in stakers:
|
|
93
|
-
stake_amount = staker.get('stake', 0)
|
|
94
|
-
if stake_amount > 0:
|
|
95
|
-
share = stake_amount / total_stake
|
|
96
|
-
reward = self.pending_fees * share
|
|
97
|
-
rewards.append({
|
|
98
|
-
'address': staker['address'],
|
|
99
|
-
'reward': reward,
|
|
100
|
-
'stake_share': share
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
return rewards
|
|
104
|
-
|
|
105
|
-
def create_distribution_transactions(self, current_block_height: int,
|
|
106
|
-
stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
107
|
-
"""Create fee distribution transactions to stakers"""
|
|
108
|
-
if not self.should_distribute(current_block_height) or self.pending_fees <= 0:
|
|
109
|
-
return []
|
|
110
|
-
|
|
111
|
-
rewards = self.calculate_rewards(stakers, total_stake)
|
|
112
|
-
distribution_txs = []
|
|
113
|
-
|
|
114
|
-
for reward_info in rewards:
|
|
115
|
-
distribution_tx = {
|
|
116
|
-
"type": "fee_distribution",
|
|
117
|
-
"from": self.fee_pool_address,
|
|
118
|
-
"to": reward_info['address'],
|
|
119
|
-
"amount": reward_info['reward'],
|
|
120
|
-
"fee": 0.0,
|
|
121
|
-
"block_height": current_block_height,
|
|
122
|
-
"distribution_cycle": current_block_height // self.distribution_blocks,
|
|
123
|
-
"stake_share": reward_info['stake_share'],
|
|
124
|
-
"timestamp": time.time(),
|
|
125
|
-
"hash": self._generate_distribution_hash(reward_info['address'], reward_info['reward'], current_block_height)
|
|
126
|
-
}
|
|
127
|
-
distribution_txs.append(distribution_tx)
|
|
128
|
-
|
|
129
|
-
# Reset pending fees after distribution
|
|
130
|
-
self.pending_fees = 0.0
|
|
131
|
-
self.last_distribution_block = current_block_height
|
|
132
|
-
|
|
133
|
-
return distribution_txs
|
|
134
|
-
|
|
135
|
-
def _generate_distribution_hash(self, address: str, amount: float, block_height: int) -> str:
|
|
136
|
-
"""Generate unique hash for distribution transaction"""
|
|
137
|
-
data = f"fee_dist_{address}_{amount}_{block_height}_{time.time()}"
|
|
138
|
-
return hashlib.sha256(data.encode()).hexdigest()
|
|
139
|
-
|
|
140
|
-
class FeeCalculator:
|
|
141
|
-
"""Configurable fee calculation system"""
|
|
142
|
-
|
|
143
|
-
def __init__(self, fee_pool_manager: FeePoolManager):
|
|
144
|
-
self.fee_pool_manager = fee_pool_manager
|
|
145
|
-
self.fee_config = {
|
|
146
|
-
'transfer': 0.00001, # Default transfer fee
|
|
147
|
-
'gtx_genesis': 0.0, # No fee for GTX genesis
|
|
148
|
-
'reward': 0.0, # No fee for rewards
|
|
149
|
-
'stake': 0.0001, # Staking fee
|
|
150
|
-
'unstake': 0.0001, # Unstaking fee
|
|
151
|
-
'delegate': 0.00005, # Delegation fee
|
|
152
|
-
'fee_distribution': 0.0, # No fee for fee distributions
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
# Dynamic fee tiers based on transaction amount
|
|
156
|
-
self.amount_tiers = {
|
|
157
|
-
'micro': (0, 1, 0.000001),
|
|
158
|
-
'small': (1, 100, 0.00001),
|
|
159
|
-
'medium': (100, 10000, 0.0001),
|
|
160
|
-
'large': (10000, 100000, 0.001),
|
|
161
|
-
'xlarge': (100000, float('inf'), 0.01)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
def set_fee(self, transaction_type: str, fee_amount: float):
|
|
165
|
-
"""Set custom fee for a transaction type"""
|
|
166
|
-
self.fee_config[transaction_type] = max(0.0, fee_amount)
|
|
167
|
-
|
|
168
|
-
def get_fee(self, transaction_type: str, amount: float = 0.0) -> float:
|
|
169
|
-
"""Get fee for transaction type and amount"""
|
|
170
|
-
base_fee = self.fee_config.get(transaction_type, 0.00001)
|
|
171
|
-
|
|
172
|
-
# Apply amount-based fee scaling
|
|
173
|
-
for tier_name, (min_amt, max_amt, tier_fee) in self.amount_tiers.items():
|
|
174
|
-
if min_amt <= amount < max_amt:
|
|
175
|
-
return max(base_fee, tier_fee)
|
|
176
|
-
|
|
177
|
-
return base_fee
|
|
178
|
-
|
|
179
|
-
def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
|
|
180
|
-
"""Calculate fee based on transaction size and priority"""
|
|
181
|
-
base_fee_per_byte = 0.0000001 # Base fee per byte
|
|
182
|
-
|
|
183
|
-
priority_multipliers = {
|
|
184
|
-
'low': 0.5,
|
|
185
|
-
'normal': 1.0,
|
|
186
|
-
'high': 2.0,
|
|
187
|
-
'urgent': 5.0
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
multiplier = priority_multipliers.get(priority, 1.0)
|
|
191
|
-
return transaction_size * base_fee_per_byte * multiplier
|
|
192
|
-
|
|
193
|
-
def process_transaction_fee(self, transaction: Dict) -> bool:
|
|
194
|
-
"""Process and collect transaction fee"""
|
|
195
|
-
fee = transaction.get('fee', 0)
|
|
196
|
-
if fee > 0:
|
|
197
|
-
return self.fee_pool_manager.collect_fee(fee, transaction.get('hash', ''))
|
|
198
|
-
return True
|
|
199
|
-
|
|
200
|
-
class TransactionManager:
|
|
201
|
-
"""Handles transaction creation, signing, and validation"""
|
|
202
|
-
|
|
203
|
-
def __init__(self):
|
|
204
|
-
self.security = TransactionSecurity()
|
|
205
|
-
self.key_manager = KeyManager()
|
|
206
|
-
self.fee_pool_manager = FeePoolManager()
|
|
207
|
-
self.fee_calculator = FeeCalculator(self.fee_pool_manager)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
"
|
|
224
|
-
"
|
|
225
|
-
"
|
|
226
|
-
"
|
|
227
|
-
"
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
transaction["
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
"
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
"
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
transaction["
|
|
344
|
-
|
|
345
|
-
str(transaction
|
|
346
|
-
str(transaction
|
|
347
|
-
transaction
|
|
348
|
-
|
|
349
|
-
transaction.get("
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
tx_copy.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
1
|
+
import time
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
from typing import Dict, Optional, Tuple, List
|
|
5
|
+
|
|
6
|
+
class TransactionSecurity:
|
|
7
|
+
"""Transaction security validation and risk assessment"""
|
|
8
|
+
|
|
9
|
+
def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
10
|
+
"""Validate transaction structure"""
|
|
11
|
+
required_fields = ['from', 'to', 'amount', 'timestamp']
|
|
12
|
+
for field in required_fields:
|
|
13
|
+
if field not in transaction:
|
|
14
|
+
return False, f'Missing required field: {field}'
|
|
15
|
+
|
|
16
|
+
# Validate amount
|
|
17
|
+
if transaction['amount'] <= 0:
|
|
18
|
+
return False, 'Amount must be positive'
|
|
19
|
+
|
|
20
|
+
return True, 'Valid'
|
|
21
|
+
|
|
22
|
+
def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
|
|
23
|
+
"""Enhanced security validation"""
|
|
24
|
+
required_fields = ['from', 'to', 'amount', 'timestamp', 'type']
|
|
25
|
+
for field in required_fields:
|
|
26
|
+
if field not in transaction:
|
|
27
|
+
return False, f'Missing required field: {field}'
|
|
28
|
+
|
|
29
|
+
# Validate amount
|
|
30
|
+
if transaction['amount'] <= 0:
|
|
31
|
+
return False, 'Invalid amount'
|
|
32
|
+
|
|
33
|
+
return True, 'Secure'
|
|
34
|
+
|
|
35
|
+
def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
36
|
+
"""Assess transaction risk level"""
|
|
37
|
+
amount = transaction.get('amount', 0)
|
|
38
|
+
|
|
39
|
+
if amount > 100000:
|
|
40
|
+
return 'high', 'Large transaction amount'
|
|
41
|
+
elif amount > 10000:
|
|
42
|
+
return 'medium', 'Medium transaction amount'
|
|
43
|
+
else:
|
|
44
|
+
return 'low', 'Normal transaction'
|
|
45
|
+
|
|
46
|
+
class KeyManager:
|
|
47
|
+
"""Key management for transaction signing"""
|
|
48
|
+
|
|
49
|
+
def derive_public_key(self, private_key: str) -> str:
|
|
50
|
+
"""Derive public key from private key"""
|
|
51
|
+
if not private_key:
|
|
52
|
+
return "default_public_key"
|
|
53
|
+
return f"pub_{private_key[-16:]}"
|
|
54
|
+
|
|
55
|
+
def sign_data(self, data: str, private_key: str) -> str:
|
|
56
|
+
"""Sign data with private key"""
|
|
57
|
+
if not private_key:
|
|
58
|
+
return "default_signature"
|
|
59
|
+
return hashlib.sha256(f"{data}{private_key}".encode()).hexdigest()
|
|
60
|
+
|
|
61
|
+
class FeePoolManager:
|
|
62
|
+
"""Decentralized fee pool management"""
|
|
63
|
+
|
|
64
|
+
def __init__(self):
|
|
65
|
+
self.fee_pool_address = self._generate_fee_pool_address()
|
|
66
|
+
self.pending_fees = 0.0
|
|
67
|
+
self.distribution_blocks = 100 # Distribute fees every 100 blocks
|
|
68
|
+
self.last_distribution_block = 0
|
|
69
|
+
|
|
70
|
+
def _generate_fee_pool_address(self) -> str:
|
|
71
|
+
"""Generate deterministic fee pool address"""
|
|
72
|
+
base_data = "LUNA_FEE_POOL_V1"
|
|
73
|
+
return hashlib.sha256(base_data.encode()).hexdigest()[:32]
|
|
74
|
+
|
|
75
|
+
def collect_fee(self, fee_amount: float, transaction_hash: str) -> bool:
|
|
76
|
+
"""Collect fee into the pool"""
|
|
77
|
+
if fee_amount > 0:
|
|
78
|
+
self.pending_fees += fee_amount
|
|
79
|
+
return True
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
def should_distribute(self, current_block_height: int) -> bool:
|
|
83
|
+
"""Check if it's time to distribute fees"""
|
|
84
|
+
return (current_block_height - self.last_distribution_block) >= self.distribution_blocks
|
|
85
|
+
|
|
86
|
+
def calculate_rewards(self, stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
87
|
+
"""Calculate rewards for stakers based on their stake"""
|
|
88
|
+
if total_stake <= 0 or self.pending_fees <= 0:
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
rewards = []
|
|
92
|
+
for staker in stakers:
|
|
93
|
+
stake_amount = staker.get('stake', 0)
|
|
94
|
+
if stake_amount > 0:
|
|
95
|
+
share = stake_amount / total_stake
|
|
96
|
+
reward = self.pending_fees * share
|
|
97
|
+
rewards.append({
|
|
98
|
+
'address': staker['address'],
|
|
99
|
+
'reward': reward,
|
|
100
|
+
'stake_share': share
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return rewards
|
|
104
|
+
|
|
105
|
+
def create_distribution_transactions(self, current_block_height: int,
|
|
106
|
+
stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
107
|
+
"""Create fee distribution transactions to stakers"""
|
|
108
|
+
if not self.should_distribute(current_block_height) or self.pending_fees <= 0:
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
rewards = self.calculate_rewards(stakers, total_stake)
|
|
112
|
+
distribution_txs = []
|
|
113
|
+
|
|
114
|
+
for reward_info in rewards:
|
|
115
|
+
distribution_tx = {
|
|
116
|
+
"type": "fee_distribution",
|
|
117
|
+
"from": self.fee_pool_address,
|
|
118
|
+
"to": reward_info['address'],
|
|
119
|
+
"amount": reward_info['reward'],
|
|
120
|
+
"fee": 0.0,
|
|
121
|
+
"block_height": current_block_height,
|
|
122
|
+
"distribution_cycle": current_block_height // self.distribution_blocks,
|
|
123
|
+
"stake_share": reward_info['stake_share'],
|
|
124
|
+
"timestamp": time.time(),
|
|
125
|
+
"hash": self._generate_distribution_hash(reward_info['address'], reward_info['reward'], current_block_height)
|
|
126
|
+
}
|
|
127
|
+
distribution_txs.append(distribution_tx)
|
|
128
|
+
|
|
129
|
+
# Reset pending fees after distribution
|
|
130
|
+
self.pending_fees = 0.0
|
|
131
|
+
self.last_distribution_block = current_block_height
|
|
132
|
+
|
|
133
|
+
return distribution_txs
|
|
134
|
+
|
|
135
|
+
def _generate_distribution_hash(self, address: str, amount: float, block_height: int) -> str:
|
|
136
|
+
"""Generate unique hash for distribution transaction"""
|
|
137
|
+
data = f"fee_dist_{address}_{amount}_{block_height}_{time.time()}"
|
|
138
|
+
return hashlib.sha256(data.encode()).hexdigest()
|
|
139
|
+
|
|
140
|
+
class FeeCalculator:
|
|
141
|
+
"""Configurable fee calculation system"""
|
|
142
|
+
|
|
143
|
+
def __init__(self, fee_pool_manager: FeePoolManager):
|
|
144
|
+
self.fee_pool_manager = fee_pool_manager
|
|
145
|
+
self.fee_config = {
|
|
146
|
+
'transfer': 0.00001, # Default transfer fee
|
|
147
|
+
'gtx_genesis': 0.0, # No fee for GTX genesis
|
|
148
|
+
'reward': 0.0, # No fee for rewards
|
|
149
|
+
'stake': 0.0001, # Staking fee
|
|
150
|
+
'unstake': 0.0001, # Unstaking fee
|
|
151
|
+
'delegate': 0.00005, # Delegation fee
|
|
152
|
+
'fee_distribution': 0.0, # No fee for fee distributions
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Dynamic fee tiers based on transaction amount
|
|
156
|
+
self.amount_tiers = {
|
|
157
|
+
'micro': (0, 1, 0.000001),
|
|
158
|
+
'small': (1, 100, 0.00001),
|
|
159
|
+
'medium': (100, 10000, 0.0001),
|
|
160
|
+
'large': (10000, 100000, 0.001),
|
|
161
|
+
'xlarge': (100000, float('inf'), 0.01)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
def set_fee(self, transaction_type: str, fee_amount: float):
|
|
165
|
+
"""Set custom fee for a transaction type"""
|
|
166
|
+
self.fee_config[transaction_type] = max(0.0, fee_amount)
|
|
167
|
+
|
|
168
|
+
def get_fee(self, transaction_type: str, amount: float = 0.0) -> float:
|
|
169
|
+
"""Get fee for transaction type and amount"""
|
|
170
|
+
base_fee = self.fee_config.get(transaction_type, 0.00001)
|
|
171
|
+
|
|
172
|
+
# Apply amount-based fee scaling
|
|
173
|
+
for tier_name, (min_amt, max_amt, tier_fee) in self.amount_tiers.items():
|
|
174
|
+
if min_amt <= amount < max_amt:
|
|
175
|
+
return max(base_fee, tier_fee)
|
|
176
|
+
|
|
177
|
+
return base_fee
|
|
178
|
+
|
|
179
|
+
def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
|
|
180
|
+
"""Calculate fee based on transaction size and priority"""
|
|
181
|
+
base_fee_per_byte = 0.0000001 # Base fee per byte
|
|
182
|
+
|
|
183
|
+
priority_multipliers = {
|
|
184
|
+
'low': 0.5,
|
|
185
|
+
'normal': 1.0,
|
|
186
|
+
'high': 2.0,
|
|
187
|
+
'urgent': 5.0
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
multiplier = priority_multipliers.get(priority, 1.0)
|
|
191
|
+
return transaction_size * base_fee_per_byte * multiplier
|
|
192
|
+
|
|
193
|
+
def process_transaction_fee(self, transaction: Dict) -> bool:
|
|
194
|
+
"""Process and collect transaction fee"""
|
|
195
|
+
fee = transaction.get('fee', 0)
|
|
196
|
+
if fee > 0:
|
|
197
|
+
return self.fee_pool_manager.collect_fee(fee, transaction.get('hash', ''))
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
class TransactionManager:
|
|
201
|
+
"""Handles transaction creation, signing, and validation"""
|
|
202
|
+
|
|
203
|
+
def __init__(self):
|
|
204
|
+
self.security = TransactionSecurity()
|
|
205
|
+
self.key_manager = KeyManager()
|
|
206
|
+
self.fee_pool_manager = FeePoolManager()
|
|
207
|
+
self.fee_calculator = FeeCalculator(self.fee_pool_manager)
|
|
208
|
+
self.pending_by_address = {} # Track pending transactions by address
|
|
209
|
+
|
|
210
|
+
def create_transaction(self, from_address: str, to_address: str, amount: float,
|
|
211
|
+
private_key: Optional[str] = None, memo: str = "",
|
|
212
|
+
transaction_type: str = "transfer", fee_override: Optional[float] = None,
|
|
213
|
+
priority: str = 'normal') -> Dict:
|
|
214
|
+
"""Create and sign a transaction with configurable fees"""
|
|
215
|
+
|
|
216
|
+
# Calculate fee
|
|
217
|
+
if fee_override is not None:
|
|
218
|
+
fee = max(0.0, fee_override)
|
|
219
|
+
else:
|
|
220
|
+
fee = self.fee_calculator.get_fee(transaction_type, amount)
|
|
221
|
+
|
|
222
|
+
transaction = {
|
|
223
|
+
"type": transaction_type,
|
|
224
|
+
"from": from_address,
|
|
225
|
+
"to": to_address,
|
|
226
|
+
"amount": float(amount),
|
|
227
|
+
"fee": fee,
|
|
228
|
+
"nonce": int(time.time() * 1000),
|
|
229
|
+
"timestamp": time.time(),
|
|
230
|
+
"memo": memo,
|
|
231
|
+
"priority": priority,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# Only add cryptographic fields if private key is provided
|
|
235
|
+
if private_key:
|
|
236
|
+
transaction["public_key"] = self.key_manager.derive_public_key(private_key)
|
|
237
|
+
sign_data = self._get_signing_data(transaction)
|
|
238
|
+
signature = self.key_manager.sign_data(sign_data, private_key)
|
|
239
|
+
transaction["signature"] = signature
|
|
240
|
+
else:
|
|
241
|
+
# For unsigned transactions (like rewards or test transactions)
|
|
242
|
+
transaction["public_key"] = "unsigned"
|
|
243
|
+
transaction["signature"] = "unsigned"
|
|
244
|
+
|
|
245
|
+
transaction["hash"] = self._calculate_transaction_hash(transaction)
|
|
246
|
+
|
|
247
|
+
# Automatically collect fee
|
|
248
|
+
self.fee_calculator.process_transaction_fee(transaction)
|
|
249
|
+
|
|
250
|
+
return transaction
|
|
251
|
+
|
|
252
|
+
def distribute_fees(self, current_block_height: int, stakers: List[Dict], total_stake: float) -> List[Dict]:
|
|
253
|
+
"""Distribute collected fees to stakers"""
|
|
254
|
+
return self.fee_pool_manager.create_distribution_transactions(
|
|
255
|
+
current_block_height, stakers, total_stake
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def get_fee_pool_balance(self) -> float:
|
|
259
|
+
"""Get current fee pool balance"""
|
|
260
|
+
return self.fee_pool_manager.pending_fees
|
|
261
|
+
|
|
262
|
+
def get_fee_pool_address(self) -> str:
|
|
263
|
+
"""Get fee pool address"""
|
|
264
|
+
return self.fee_pool_manager.fee_pool_address
|
|
265
|
+
|
|
266
|
+
def create_gtx_transaction(self, bill_info: Dict) -> Dict:
|
|
267
|
+
"""Create GTX Genesis transaction from mined bill"""
|
|
268
|
+
# Ensure we return a proper transaction structure
|
|
269
|
+
if "transaction_data" in bill_info:
|
|
270
|
+
return bill_info["transaction_data"]
|
|
271
|
+
else:
|
|
272
|
+
# Fallback: create basic transaction from bill info
|
|
273
|
+
return {
|
|
274
|
+
"type": "gtx_genesis",
|
|
275
|
+
"from": "mining",
|
|
276
|
+
"to": bill_info.get("owner_address", "unknown"),
|
|
277
|
+
"amount": bill_info.get("denomination", 0),
|
|
278
|
+
"fee": 0.0, # No fee for GTX genesis
|
|
279
|
+
"timestamp": time.time(),
|
|
280
|
+
"hash": f"gtx_{hashlib.sha256(json.dumps(bill_info).encode()).hexdigest()[:16]}"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
def create_reward_transaction(self, to_address: str, amount: float, block_height: int) -> Dict:
|
|
284
|
+
"""Create block reward transaction"""
|
|
285
|
+
transaction = {
|
|
286
|
+
"type": "reward",
|
|
287
|
+
"from": "network",
|
|
288
|
+
"to": to_address,
|
|
289
|
+
"amount": float(amount),
|
|
290
|
+
"fee": 0.0, # No fee for rewards
|
|
291
|
+
"block_height": block_height,
|
|
292
|
+
"timestamp": time.time(),
|
|
293
|
+
"hash": self._generate_reward_hash(to_address, amount, block_height)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return transaction
|
|
297
|
+
|
|
298
|
+
def create_stake_transaction(self, from_address: str, amount: float,
|
|
299
|
+
private_key: Optional[str] = None) -> Dict:
|
|
300
|
+
"""Create staking transaction"""
|
|
301
|
+
return self.create_transaction(
|
|
302
|
+
from_address=from_address,
|
|
303
|
+
to_address="staking_pool",
|
|
304
|
+
amount=amount,
|
|
305
|
+
private_key=private_key,
|
|
306
|
+
transaction_type="stake"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def create_unstake_transaction(self, from_address: str, amount: float,
|
|
310
|
+
private_key: Optional[str] = None) -> Dict:
|
|
311
|
+
"""Create unstaking transaction"""
|
|
312
|
+
return self.create_transaction(
|
|
313
|
+
from_address="staking_pool",
|
|
314
|
+
to_address=from_address,
|
|
315
|
+
amount=amount,
|
|
316
|
+
private_key=private_key,
|
|
317
|
+
transaction_type="unstake"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
|
|
321
|
+
"""Validate transaction using security module"""
|
|
322
|
+
return self.security.validate_transaction(transaction)
|
|
323
|
+
|
|
324
|
+
def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
|
|
325
|
+
"""Validate transaction security"""
|
|
326
|
+
return self.security.validate_transaction_security(transaction)
|
|
327
|
+
|
|
328
|
+
def assess_transaction_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
329
|
+
"""Assess transaction risk level"""
|
|
330
|
+
return self.security.assess_risk(transaction)
|
|
331
|
+
|
|
332
|
+
def set_transaction_fee(self, transaction_type: str, fee_amount: float):
|
|
333
|
+
"""Set custom fee for transaction type"""
|
|
334
|
+
self.fee_calculator.set_fee(transaction_type, fee_amount)
|
|
335
|
+
|
|
336
|
+
def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
|
|
337
|
+
"""Calculate network fee based on size and priority"""
|
|
338
|
+
return self.fee_calculator.calculate_network_fee(transaction_size, priority)
|
|
339
|
+
|
|
340
|
+
def _get_signing_data(self, transaction: Dict) -> str:
|
|
341
|
+
"""Create data string for signing"""
|
|
342
|
+
parts = [
|
|
343
|
+
transaction["from"],
|
|
344
|
+
transaction["to"],
|
|
345
|
+
str(transaction["amount"]),
|
|
346
|
+
str(transaction.get("nonce", 0)),
|
|
347
|
+
str(transaction["timestamp"]),
|
|
348
|
+
transaction.get("memo", ""),
|
|
349
|
+
str(transaction.get("fee", 0)),
|
|
350
|
+
transaction.get("type", "transfer")
|
|
351
|
+
]
|
|
352
|
+
return "".join(parts)
|
|
353
|
+
|
|
354
|
+
def _calculate_transaction_hash(self, transaction: Dict) -> str:
|
|
355
|
+
"""Calculate transaction hash"""
|
|
356
|
+
# Create a copy without signature for consistent hashing
|
|
357
|
+
tx_copy = transaction.copy()
|
|
358
|
+
tx_copy.pop("signature", None)
|
|
359
|
+
data_string = json.dumps(tx_copy, sort_keys=True)
|
|
360
|
+
return hashlib.sha256(data_string.encode()).hexdigest() # FIXED: data_string instead of data
|
|
361
|
+
|
|
362
|
+
def _generate_reward_hash(self, to_address: str, amount: float, block_height: int) -> str:
|
|
363
|
+
"""Generate unique hash for reward transaction"""
|
|
364
|
+
data = f"reward_{to_address}_{amount}_{block_height}_{time.time()}"
|
|
365
|
+
return hashlib.sha256(data.encode()).hexdigest()
|
|
366
|
+
|
|
367
|
+
def validate_against_available_balance(self, from_address: str, amount: float,
|
|
368
|
+
available_balance: float) -> Tuple[bool, str]:
|
|
369
|
+
"""Validate that transaction amount doesn't exceed available balance"""
|
|
370
|
+
pending_amount = self.pending_by_address.get(from_address, 0.0)
|
|
371
|
+
total_pending = pending_amount + amount
|
|
372
|
+
|
|
373
|
+
if total_pending > available_balance:
|
|
374
|
+
shortage = total_pending - available_balance
|
|
375
|
+
return False, f'Insufficient available balance. Need {shortage} more (accounting for {pending_amount} in pending transactions)'
|
|
376
|
+
|
|
377
|
+
return True, 'Sufficient balance'
|
|
378
|
+
|
|
379
|
+
def add_pending_transaction(self, from_address: str, tx_hash: str, amount: float) -> bool:
|
|
380
|
+
"""Track a pending transaction (added to mempool)"""
|
|
381
|
+
if from_address not in self.pending_by_address:
|
|
382
|
+
self.pending_by_address[from_address] = 0.0
|
|
383
|
+
|
|
384
|
+
self.pending_by_address[from_address] += float(amount)
|
|
385
|
+
return True
|
|
386
|
+
|
|
387
|
+
def confirm_transaction(self, from_address: str, amount: float) -> bool:
|
|
388
|
+
"""Confirm a transaction (moved from mempool to blockchain)"""
|
|
389
|
+
if from_address in self.pending_by_address:
|
|
390
|
+
pending = self.pending_by_address[from_address]
|
|
391
|
+
new_pending = max(0.0, pending - float(amount))
|
|
392
|
+
|
|
393
|
+
if new_pending == 0:
|
|
394
|
+
del self.pending_by_address[from_address]
|
|
395
|
+
else:
|
|
396
|
+
self.pending_by_address[from_address] = new_pending
|
|
397
|
+
|
|
398
|
+
return True
|
|
399
|
+
return False
|
|
400
|
+
|
|
401
|
+
def get_pending_amount(self, from_address: str) -> float:
|
|
402
|
+
"""Get total pending transaction amount for address"""
|
|
403
|
+
return self.pending_by_address.get(from_address, 0.0)
|
|
404
|
+
|
|
405
|
+
def clear_pending_for_address(self, from_address: str) -> bool:
|
|
406
|
+
"""Clear all pending transactions for address (e.g., if they failed)"""
|
|
407
|
+
if from_address in self.pending_by_address:
|
|
408
|
+
del self.pending_by_address[from_address]
|
|
409
|
+
return True
|
|
410
|
+
|
|
411
|
+
# Additional validator class for backward compatibility
|
|
412
|
+
class TransactionValidator:
|
|
413
|
+
"""Transaction validator for risk assessment and validation"""
|
|
414
|
+
|
|
415
|
+
def __init__(self):
|
|
416
|
+
self.security = TransactionSecurity()
|
|
417
|
+
|
|
418
|
+
def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
|
|
419
|
+
"""Assess transaction risk level"""
|
|
420
|
+
return self.security.assess_risk(transaction)
|
|
421
|
+
|
|
422
|
+
def validate(self, transaction: Dict) -> Tuple[bool, str]:
|
|
423
|
+
"""Validate transaction"""
|
|
379
424
|
return self.security.validate_transaction(transaction)
|