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.
@@ -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
- def create_transaction(self, from_address: str, to_address: str, amount: float,
210
- private_key: Optional[str] = None, memo: str = "",
211
- transaction_type: str = "transfer", fee_override: Optional[float] = None,
212
- priority: str = 'normal') -> Dict:
213
- """Create and sign a transaction with configurable fees"""
214
-
215
- # Calculate fee
216
- if fee_override is not None:
217
- fee = max(0.0, fee_override)
218
- else:
219
- fee = self.fee_calculator.get_fee(transaction_type, amount)
220
-
221
- transaction = {
222
- "type": transaction_type,
223
- "from": from_address,
224
- "to": to_address,
225
- "amount": float(amount),
226
- "fee": fee,
227
- "nonce": int(time.time() * 1000),
228
- "timestamp": time.time(),
229
- "memo": memo,
230
- "priority": priority,
231
- }
232
-
233
- # Only add cryptographic fields if private key is provided
234
- if private_key:
235
- transaction["public_key"] = self.key_manager.derive_public_key(private_key)
236
- sign_data = self._get_signing_data(transaction)
237
- signature = self.key_manager.sign_data(sign_data, private_key)
238
- transaction["signature"] = signature
239
- else:
240
- # For unsigned transactions (like rewards or test transactions)
241
- transaction["public_key"] = "unsigned"
242
- transaction["signature"] = "unsigned"
243
-
244
- transaction["hash"] = self._calculate_transaction_hash(transaction)
245
-
246
- # Automatically collect fee
247
- self.fee_calculator.process_transaction_fee(transaction)
248
-
249
- return transaction
250
-
251
- def distribute_fees(self, current_block_height: int, stakers: List[Dict], total_stake: float) -> List[Dict]:
252
- """Distribute collected fees to stakers"""
253
- return self.fee_pool_manager.create_distribution_transactions(
254
- current_block_height, stakers, total_stake
255
- )
256
-
257
- def get_fee_pool_balance(self) -> float:
258
- """Get current fee pool balance"""
259
- return self.fee_pool_manager.pending_fees
260
-
261
- def get_fee_pool_address(self) -> str:
262
- """Get fee pool address"""
263
- return self.fee_pool_manager.fee_pool_address
264
-
265
- def create_gtx_transaction(self, bill_info: Dict) -> Dict:
266
- """Create GTX Genesis transaction from mined bill"""
267
- # Ensure we return a proper transaction structure
268
- if "transaction_data" in bill_info:
269
- return bill_info["transaction_data"]
270
- else:
271
- # Fallback: create basic transaction from bill info
272
- return {
273
- "type": "gtx_genesis",
274
- "from": "mining",
275
- "to": bill_info.get("owner_address", "unknown"),
276
- "amount": bill_info.get("denomination", 0),
277
- "fee": 0.0, # No fee for GTX genesis
278
- "timestamp": time.time(),
279
- "hash": f"gtx_{hashlib.sha256(json.dumps(bill_info).encode()).hexdigest()[:16]}"
280
- }
281
-
282
- def create_reward_transaction(self, to_address: str, amount: float, block_height: int) -> Dict:
283
- """Create block reward transaction"""
284
- transaction = {
285
- "type": "reward",
286
- "from": "network",
287
- "to": to_address,
288
- "amount": float(amount),
289
- "fee": 0.0, # No fee for rewards
290
- "block_height": block_height,
291
- "timestamp": time.time(),
292
- "hash": self._generate_reward_hash(to_address, amount, block_height)
293
- }
294
-
295
- return transaction
296
-
297
- def create_stake_transaction(self, from_address: str, amount: float,
298
- private_key: Optional[str] = None) -> Dict:
299
- """Create staking transaction"""
300
- return self.create_transaction(
301
- from_address=from_address,
302
- to_address="staking_pool",
303
- amount=amount,
304
- private_key=private_key,
305
- transaction_type="stake"
306
- )
307
-
308
- def create_unstake_transaction(self, from_address: str, amount: float,
309
- private_key: Optional[str] = None) -> Dict:
310
- """Create unstaking transaction"""
311
- return self.create_transaction(
312
- from_address="staking_pool",
313
- to_address=from_address,
314
- amount=amount,
315
- private_key=private_key,
316
- transaction_type="unstake"
317
- )
318
-
319
- def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
320
- """Validate transaction using security module"""
321
- return self.security.validate_transaction(transaction)
322
-
323
- def validate_transaction_security(self, transaction: Dict) -> Tuple[bool, str]:
324
- """Validate transaction security"""
325
- return self.security.validate_transaction_security(transaction)
326
-
327
- def assess_transaction_risk(self, transaction: Dict) -> Tuple[str, str]:
328
- """Assess transaction risk level"""
329
- return self.security.assess_risk(transaction)
330
-
331
- def set_transaction_fee(self, transaction_type: str, fee_amount: float):
332
- """Set custom fee for transaction type"""
333
- self.fee_calculator.set_fee(transaction_type, fee_amount)
334
-
335
- def calculate_network_fee(self, transaction_size: int, priority: str = 'normal') -> float:
336
- """Calculate network fee based on size and priority"""
337
- return self.fee_calculator.calculate_network_fee(transaction_size, priority)
338
-
339
- def _get_signing_data(self, transaction: Dict) -> str:
340
- """Create data string for signing"""
341
- parts = [
342
- transaction["from"],
343
- transaction["to"],
344
- str(transaction["amount"]),
345
- str(transaction.get("nonce", 0)),
346
- str(transaction["timestamp"]),
347
- transaction.get("memo", ""),
348
- str(transaction.get("fee", 0)),
349
- transaction.get("type", "transfer")
350
- ]
351
- return "".join(parts)
352
-
353
- def _calculate_transaction_hash(self, transaction: Dict) -> str:
354
- """Calculate transaction hash"""
355
- # Create a copy without signature for consistent hashing
356
- tx_copy = transaction.copy()
357
- tx_copy.pop("signature", None)
358
- data_string = json.dumps(tx_copy, sort_keys=True)
359
- return hashlib.sha256(data_string.encode()).hexdigest() # FIXED: data_string instead of data
360
-
361
- def _generate_reward_hash(self, to_address: str, amount: float, block_height: int) -> str:
362
- """Generate unique hash for reward transaction"""
363
- data = f"reward_{to_address}_{amount}_{block_height}_{time.time()}"
364
- return hashlib.sha256(data.encode()).hexdigest()
365
-
366
- # Additional validator class for backward compatibility
367
- class TransactionValidator:
368
- """Transaction validator for risk assessment and validation"""
369
-
370
- def __init__(self):
371
- self.security = TransactionSecurity()
372
-
373
- def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
374
- """Assess transaction risk level"""
375
- return self.security.assess_risk(transaction)
376
-
377
- def validate(self, transaction: Dict) -> Tuple[bool, str]:
378
- """Validate transaction"""
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)