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.
@@ -5,272 +5,82 @@ import json
5
5
  from typing import Dict, Optional, Tuple, List
6
6
  from ..core.mempool import MempoolManager
7
7
 
8
+ # Import REAL SM2 KeyManager from crypto module
9
+ try:
10
+ from ..core.crypto import KeyManager as SM2KeyManager
11
+ SM2_AVAILABLE = True
12
+ print("DEBUG: Using SM2 KeyManager from crypto module")
13
+ except ImportError as e:
14
+ SM2_AVAILABLE = False
15
+ print(f"WARNING: SM2 KeyManager not available: {e}")
16
+
8
17
  class TransactionSecurity:
9
18
  """Transaction security validation and risk assessment"""
10
19
 
11
20
  def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
12
21
  """Validate transaction structure"""
13
- required_fields = ['type', 'from', 'to', 'amount', 'timestamp']
22
+ required_fields = ['type', 'from', 'to', 'amount', 'timestamp', 'hash']
14
23
  for field in required_fields:
15
24
  if field not in transaction:
16
25
  return False, f'Missing required field: {field}'
17
26
 
18
27
  # Validate amount
19
- if transaction['amount'] <= 0:
28
+ if transaction['amount'] <= 0 and transaction['type'] != 'reward':
20
29
  return False, 'Amount must be positive'
21
30
 
22
31
  return True, 'Valid'
23
32
 
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
33
  def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
45
34
  """Assess transaction risk level"""
46
35
  amount = transaction.get('amount', 0)
47
36
  tx_type = transaction.get('type', 'transfer')
48
37
 
49
- if tx_type in ['gtx_genesis', 'reward', 'fee_distribution']:
38
+ if tx_type in ['gtx_genesis', 'reward']:
50
39
  return 'very_low', 'System transaction'
51
40
 
52
41
  if amount > 1000000:
53
- return 'high', 'Very large transaction amount'
42
+ return 'high', 'Very large transaction'
54
43
  elif amount > 100000:
55
- return 'high', 'Large transaction amount'
56
- elif amount > 10000:
57
- return 'medium', 'Medium transaction amount'
58
- elif amount > 1000:
59
- return 'low', 'Small transaction amount'
44
+ return 'medium', 'Large transaction'
60
45
  else:
61
- return 'very_low', 'Normal transaction'
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
- }
46
+ return 'low', 'Normal transaction'
189
47
 
190
48
  class FeeCalculator:
191
- """Configurable fee calculation system"""
49
+ """Simple fee calculation"""
192
50
 
193
- def __init__(self, fee_pool_manager: FeePoolManager):
194
- self.fee_pool_manager = fee_pool_manager
51
+ def __init__(self):
195
52
  self.fee_config = {
196
- 'transfer': 0.00001, # Default transfer fee
197
- 'stake': 0.0001, # Staking fee
198
- 'unstake': 0.0001, # Unstaking fee
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
53
+ 'transfer': 0.001,
54
+ 'reward': 0.0,
55
+ 'gtx_genesis': 0.0
241
56
  }
242
-
243
- multiplier = priority_multipliers.get(priority, 1.0)
244
- return transaction_size * base_fee_per_byte * multiplier
245
57
 
246
- def process_transaction_fee(self, transaction: Dict) -> bool:
247
- """Process and collect transaction fee"""
248
- fee = transaction.get('fee', 0)
249
- if fee > 0:
250
- return self.fee_pool_manager.collect_fee(fee, transaction.get('hash', ''))
251
- return True
58
+ def get_fee(self, transaction_type: str) -> float:
59
+ """Get fee for transaction type"""
60
+ return self.fee_config.get(transaction_type, 0.001)
252
61
 
253
62
  class TransactionManager:
254
63
  """Handles transaction creation, signing, validation, and broadcasting"""
255
64
 
256
65
  def __init__(self, network_endpoints: List[str] = None):
257
66
  self.security = TransactionSecurity()
258
- self.key_manager = KeyManager()
259
- self.fee_pool_manager = FeePoolManager()
260
- self.fee_calculator = FeeCalculator(self.fee_pool_manager)
67
+ self.fee_calculator = FeeCalculator()
261
68
  self.mempool_manager = MempoolManager(network_endpoints)
69
+
70
+ # Initialize SM2 KeyManager if available
71
+ if SM2_AVAILABLE:
72
+ self.key_manager = SM2KeyManager()
73
+ else:
74
+ print("ERROR: SM2 KeyManager not available - cannot sign transactions")
75
+ self.key_manager = None
262
76
 
263
77
  def create_transaction(self, from_address: str, to_address: str, amount: float,
264
- private_key: Optional[str] = None, memo: str = "",
265
- transaction_type: str = "transfer", fee_override: Optional[float] = None,
266
- priority: str = 'normal') -> Dict:
267
- """Create and sign a transaction with configurable fees"""
78
+ private_key: Optional[str] = None, memo: str = "",
79
+ transaction_type: str = "transfer") -> Dict:
80
+ """Create and sign a transaction"""
268
81
 
269
82
  # Calculate fee
270
- if fee_override is not None:
271
- fee = max(0.0, fee_override)
272
- else:
273
- fee = self.fee_calculator.get_fee(transaction_type, amount)
83
+ fee = self.fee_calculator.get_fee(transaction_type)
274
84
 
275
85
  transaction = {
276
86
  "type": transaction_type,
@@ -278,29 +88,56 @@ class TransactionManager:
278
88
  "to": to_address,
279
89
  "amount": float(amount),
280
90
  "fee": fee,
281
- "nonce": int(time.time() * 1000),
282
- "timestamp": time.time(),
91
+ "timestamp": int(time.time()),
283
92
  "memo": memo,
284
- "priority": priority,
285
- "version": "1.0"
93
+ "version": "2.0" # Version 2.0 = SM2 signatures
286
94
  }
287
95
 
288
- # Only add cryptographic fields if private key is provided
289
- if private_key:
290
- transaction["public_key"] = self.key_manager.derive_public_key(private_key)
291
- sign_data = self._get_signing_data(transaction)
292
- signature = self.key_manager.sign_data(sign_data, private_key)
293
- transaction["signature"] = signature
96
+ # Sign transaction with SM2 if private key provided
97
+ if private_key and self.key_manager:
98
+ try:
99
+ # Sign the transaction data
100
+ tx_string = self._get_signing_data(transaction)
101
+
102
+ print(f"[TRANSACTIONS CREATE DEBUG] Signing data: {tx_string}")
103
+ print(f"[TRANSACTIONS CREATE DEBUG] Private key available: {bool(private_key)}")
104
+ print(f"[TRANSACTIONS CREATE DEBUG] Private key length: {len(private_key)}")
105
+
106
+ signature = self.key_manager.sign_data(tx_string, private_key)
107
+
108
+ # Get public key from private key
109
+ public_key = self.key_manager.derive_public_key(private_key)
110
+
111
+ print(f"[TRANSACTIONS CREATE DEBUG] Generated signature: {signature}")
112
+ print(f"[TRANSACTIONS CREATE DEBUG] Generated public key: {public_key}")
113
+ print(f"[TRANSACTIONS CREATE DEBUG] Signature length: {len(signature)}")
114
+ print(f"[TRANSACTIONS CREATE DEBUG] Public key length: {len(public_key)}")
115
+
116
+ transaction["signature"] = signature
117
+ transaction["public_key"] = public_key
118
+
119
+ # Immediately test verification
120
+ test_verify = self.key_manager.verify_signature(tx_string, signature, public_key)
121
+ print(f"[TRANSACTIONS CREATE DEBUG] Immediate self-verification: {test_verify}")
122
+
123
+ if not test_verify:
124
+ print(f"[TRANSACTIONS CREATE ERROR] Signature doesn't verify immediately!")
125
+ print(f"[TRANSACTIONS CREATE ERROR] This suggests an SM2 implementation issue")
126
+
127
+ except Exception as e:
128
+ print(f"[TRANSACTIONS CREATE ERROR] Signing failed: {e}")
129
+ import traceback
130
+ traceback.print_exc()
131
+ transaction["signature"] = "unsigned"
132
+ transaction["public_key"] = "unsigned"
294
133
  else:
295
- # For unsigned transactions (like rewards or system transactions)
296
- transaction["public_key"] = "unsigned"
134
+ # For unsigned transactions (rewards, gtx_genesis)
297
135
  transaction["signature"] = "unsigned"
136
+ transaction["public_key"] = "unsigned"
298
137
 
138
+ # Calculate transaction hash (must be last)
299
139
  transaction["hash"] = self._calculate_transaction_hash(transaction)
300
140
 
301
- # Automatically collect fee
302
- self.fee_calculator.process_transaction_fee(transaction)
303
-
304
141
  return transaction
305
142
 
306
143
  def send_transaction(self, transaction: Dict) -> Tuple[bool, str]:
@@ -311,19 +148,24 @@ class TransactionManager:
311
148
  if not is_valid:
312
149
  return False, f"Validation failed: {message}"
313
150
 
151
+ # Verify signature for non-system transactions
152
+ if transaction["type"] == "transfer":
153
+ if not self.verify_transaction_signature(transaction):
154
+ return False, "Invalid transaction signature"
155
+
314
156
  # Add to mempool
315
157
  success = self.mempool_manager.add_transaction(transaction)
316
158
  if success:
317
- return True, f"Transaction added to mempool: {transaction.get('hash')}"
159
+ return True, f"Transaction added to mempool: {transaction.get('hash', '')[:16]}..."
318
160
  else:
319
161
  return False, "Failed to add transaction to mempool"
320
162
 
321
163
  except Exception as e:
322
164
  return False, f"Error sending transaction: {str(e)}"
323
165
 
324
- # TRANSFER TRANSACTIONS
166
+ # TRANSFER TRANSACTION
325
167
  def create_transfer(self, from_address: str, to_address: str, amount: float,
326
- private_key: str, memo: str = "", fee_override: Optional[float] = None) -> Dict:
168
+ private_key: str, memo: str = "") -> Dict:
327
169
  """Create a transfer transaction"""
328
170
  return self.create_transaction(
329
171
  from_address=from_address,
@@ -331,109 +173,30 @@ class TransactionManager:
331
173
  amount=amount,
332
174
  private_key=private_key,
333
175
  memo=memo,
334
- transaction_type="transfer",
335
- fee_override=fee_override
176
+ transaction_type="transfer"
336
177
  )
337
178
 
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"
409
- )
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
-
418
- # SYSTEM TRANSACTIONS
179
+ # SYSTEM TRANSACTIONS (unsigned)
419
180
  def create_gtx_transaction(self, bill_info: Dict) -> Dict:
420
181
  """Create GTX Genesis transaction from mined bill"""
421
- return {
182
+ transaction = {
422
183
  "type": "gtx_genesis",
423
184
  "from": "mining",
424
185
  "to": bill_info.get("owner_address", "unknown"),
425
- "amount": bill_info.get("denomination", 0),
186
+ "amount": float(bill_info.get("denomination", 0)),
426
187
  "fee": 0.0,
427
- "timestamp": time.time(),
188
+ "timestamp": int(time.time()),
428
189
  "bill_serial": bill_info.get("serial", ""),
429
190
  "mining_difficulty": bill_info.get("difficulty", 0),
430
- "hash": f"gtx_{hashlib.sha256(json.dumps(bill_info).encode()).hexdigest()[:16]}",
191
+ "signature": "system",
431
192
  "public_key": "system",
432
- "signature": "system"
193
+ "version": "2.0"
433
194
  }
195
+ transaction["hash"] = self._calculate_transaction_hash(transaction)
196
+ return transaction
434
197
 
435
198
  def create_reward_transaction(self, to_address: str, amount: float,
436
- block_height: int, reward_type: str = "block") -> Dict:
199
+ block_height: int) -> Dict:
437
200
  """Create reward transaction"""
438
201
  transaction = {
439
202
  "type": "reward",
@@ -442,61 +205,122 @@ class TransactionManager:
442
205
  "amount": float(amount),
443
206
  "fee": 0.0,
444
207
  "block_height": block_height,
445
- "reward_type": reward_type, # block, fee, staking
446
- "timestamp": time.time(),
447
- "hash": self._generate_reward_hash(to_address, amount, block_height, reward_type),
208
+ "timestamp": int(time.time()),
209
+ "signature": "system",
448
210
  "public_key": "system",
449
- "signature": "system"
211
+ "version": "2.0"
450
212
  }
213
+ transaction["hash"] = self._generate_reward_hash(to_address, amount, block_height)
451
214
  return transaction
452
215
 
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
216
  # VALIDATION METHODS
479
217
  def validate_transaction(self, transaction: Dict) -> Tuple[bool, str]:
480
218
  """Validate transaction using security module"""
481
219
  return self.security.validate_transaction(transaction)
482
220
 
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
221
  def verify_transaction_signature(self, transaction: Dict) -> bool:
492
- """Verify transaction signature"""
222
+ """Verify transaction signature with SM2"""
493
223
  try:
494
- sign_data = self._get_signing_data(transaction)
495
224
  signature = transaction.get("signature", "")
225
+ tx_type = transaction.get("type", "").lower()
226
+
227
+ # System transactions are always valid
228
+ if signature in ["system", "unsigned", "test"]:
229
+ print(f"[TRANSACTIONS] Skipping signature check for {signature} transaction")
230
+ return True
231
+
232
+ if not self.key_manager:
233
+ print("[TRANSACTIONS] No key manager available for verification")
234
+ return False
235
+
236
+ # Check SM2 signature format
237
+ if len(signature) != 128:
238
+ print(f"[TRANSACTIONS] Invalid SM2 signature length: {len(signature)} (expected 128)")
239
+ return False
240
+
241
+ # Get signing data (without public_key!)
242
+ sign_data = self._get_signing_data(transaction)
496
243
  public_key = transaction.get("public_key", "")
497
- return self.key_manager.verify_signature(sign_data, signature, public_key)
498
- except:
244
+
245
+ print(f"[TRANSACTIONS VERIFY] Signing data length: {len(sign_data)}")
246
+ print(f"[TRANSACTIONS VERIFY] Signing data (first 100 chars): {sign_data[:100]}")
247
+
248
+ # Try to verify with KeyManager
249
+ print(f"[TRANSACTIONS] Attempting verification...")
250
+ is_valid = self.key_manager.verify_signature(sign_data, signature, public_key)
251
+
252
+ print(f"[TRANSACTIONS] SM2 signature verification result: {is_valid}")
253
+
254
+ return is_valid
255
+
256
+ except Exception as e:
257
+ print(f"[TRANSACTIONS] Verification error: {e}")
258
+ import traceback
259
+ traceback.print_exc()
499
260
  return False
261
+ def _debug_signature_issue(self, transaction: Dict, sign_data: str, signature: str, public_key: str):
262
+ """Debug why signature verification is failing"""
263
+ print("\n" + "="*60)
264
+ print("DEBUGGING SIGNATURE ISSUE")
265
+ print("="*60)
266
+
267
+ # 1. Check if we can sign and verify a simple test
268
+ print("\n1. Testing SM2 with simple message...")
269
+ test_message = "Simple test message"
270
+ test_private = self.key_manager.generate_private_key()
271
+ test_public = self.key_manager.derive_public_key(test_private)
272
+ test_sig = self.key_manager.sign_data(test_message, test_private)
273
+ test_valid = self.key_manager.verify_signature(test_message, test_sig, test_public)
274
+ print(f" Simple test verification: {test_valid}")
275
+
276
+ # 2. Try to recreate what was signed during transaction creation
277
+ print("\n2. Reconstructing original transaction data...")
278
+ # Create the exact transaction data that should have been signed
279
+ reconstructed = {
280
+ "amount": float(transaction["amount"]),
281
+ "fee": float(transaction["fee"]),
282
+ "from": transaction["from"],
283
+ "memo": transaction.get("memo", ""),
284
+ "timestamp": int(transaction["timestamp"]),
285
+ "to": transaction["to"],
286
+ "type": transaction["type"],
287
+ "version": transaction.get("version", "2.0")
288
+ }
289
+
290
+ import json
291
+ reconstructed_json = json.dumps(reconstructed, sort_keys=True)
292
+ print(f" Reconstructed JSON: {reconstructed_json}")
293
+ print(f" Current signing data: {sign_data}")
294
+ print(f" Are they equal? {reconstructed_json == sign_data}")
295
+ print(f" Length difference: {len(reconstructed_json)} vs {len(sign_data)}")
296
+
297
+ # 3. Check for whitespace differences
298
+ print("\n3. Checking for whitespace differences...")
299
+ print(f" Reconstructed has spaces: {' ' in reconstructed_json}")
300
+ print(f" Sign data has spaces: {' ' in sign_data}")
301
+
302
+ # 4. Check float formatting
303
+ print("\n4. Checking float formatting...")
304
+ print(f" Amount in tx: {transaction['amount']} (type: {type(transaction['amount'])})")
305
+ print(f" Amount in reconstructed: {reconstructed['amount']} (type: {type(reconstructed['amount'])})")
306
+
307
+ # 5. Try different JSON serialization options
308
+ print("\n5. Trying different JSON formats...")
309
+ formats = [
310
+ ("Compact", lambda x: json.dumps(x, sort_keys=True, separators=(',', ':'))),
311
+ ("Default", lambda x: json.dumps(x, sort_keys=True)),
312
+ ("Indented", lambda x: json.dumps(x, sort_keys=True, indent=2)),
313
+ ]
314
+
315
+ for name, formatter in formats:
316
+ formatted = formatter(reconstructed)
317
+ is_valid_test = self.key_manager.verify_signature(formatted, signature, public_key)
318
+ print(f" {name} format: {is_valid_test} (length: {len(formatted)})")
319
+
320
+ print("="*60 + "\n")
321
+ def assess_transaction_risk(self, transaction: Dict) -> Tuple[str, str]:
322
+ """Assess transaction risk level"""
323
+ return self.security.assess_risk(transaction)
500
324
 
501
325
  # MEMPOOL MANAGEMENT
502
326
  def get_pending_transactions(self, address: str = None) -> List[Dict]:
@@ -507,77 +331,69 @@ class TransactionManager:
507
331
  """Check if transaction is pending in mempool"""
508
332
  return self.mempool_manager.is_transaction_pending(tx_hash)
509
333
 
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
334
  def _get_signing_data(self, transaction: Dict) -> str:
537
- """Create data string for signing"""
538
- parts = [
539
- transaction["type"],
540
- transaction["from"],
541
- transaction["to"],
542
- str(transaction["amount"]),
543
- str(transaction.get("nonce", 0)),
544
- str(transaction["timestamp"]),
545
- transaction.get("memo", ""),
546
- str(transaction.get("fee", 0)),
547
- transaction.get("version", "1.0")
548
- ]
549
- return "|".join(parts)
550
-
335
+ """Create data string for signing - MUST match signing process exactly"""
336
+ # IMPORTANT: During signing, the transaction does NOT have 'public_key' yet!
337
+ # It's added AFTER signing. So we must exclude it during verification too.
338
+
339
+ # Fields that should NOT be included in signing data:
340
+ exclude_fields = ['signature', 'hash', 'public_key']
341
+
342
+ # Create copy without excluded fields
343
+ tx_copy = {k: v for k, v in transaction.items()
344
+ if k not in exclude_fields}
345
+
346
+ # Use the EXACT same format as during creation
347
+ import json
348
+
349
+ # Ensure consistent data types
350
+ for key in ['amount', 'fee']:
351
+ if key in tx_copy:
352
+ tx_copy[key] = float(tx_copy[key])
353
+
354
+ if 'timestamp' in tx_copy:
355
+ tx_copy['timestamp'] = int(tx_copy['timestamp'])
356
+
357
+ # Use COMPACT JSON (no extra spaces) - this is critical!
358
+ return json.dumps(tx_copy, sort_keys=True, separators=(',', ':'))
551
359
  def _calculate_transaction_hash(self, transaction: Dict) -> str:
552
360
  """Calculate transaction hash"""
553
- # Create a copy without signature for consistent hashing
361
+ # Remove existing hash if present
554
362
  tx_copy = transaction.copy()
555
- tx_copy.pop("signature", None)
556
363
  tx_copy.pop("hash", None)
364
+
365
+ # Convert to JSON and hash
557
366
  data_string = json.dumps(tx_copy, sort_keys=True)
558
367
  return hashlib.sha256(data_string.encode()).hexdigest()
559
368
 
560
369
  def _generate_reward_hash(self, to_address: str, amount: float,
561
- block_height: int, reward_type: str) -> str:
370
+ block_height: int) -> str:
562
371
  """Generate unique hash for reward transaction"""
563
- data = f"reward_{reward_type}_{to_address}_{amount}_{block_height}_{time.time_ns()}"
372
+ data = f"reward_{to_address}_{amount}_{block_height}_{time.time_ns()}"
564
373
  return hashlib.sha256(data.encode()).hexdigest()
565
374
 
566
375
  def stop(self):
567
376
  """Stop the transaction manager"""
568
377
  self.mempool_manager.stop()
569
378
 
570
- # Additional validator class for backward compatibility
571
- class TransactionValidator:
572
- """Transaction validator for risk assessment and validation"""
573
-
574
- def __init__(self):
575
- self.security = TransactionSecurity()
576
-
577
- def assess_risk(self, transaction: Dict) -> Tuple[str, str]:
578
- """Assess transaction risk level"""
579
- return self.security.assess_risk(transaction)
580
-
581
- def validate(self, transaction: Dict) -> Tuple[bool, str]:
582
- """Validate transaction"""
583
- return self.security.validate_transaction(transaction)
379
+ # Create a global instance for convenience
380
+ _transaction_manager = None
381
+
382
+ def get_transaction_manager() -> TransactionManager:
383
+ """Get or create global transaction manager instance"""
384
+ global _transaction_manager
385
+ if _transaction_manager is None:
386
+ _transaction_manager = TransactionManager()
387
+ return _transaction_manager
388
+
389
+ # Convenience functions
390
+ def create_transfer(from_address: str, to_address: str, amount: float,
391
+ private_key: str, memo: str = "") -> Dict:
392
+ """Create transfer transaction (convenience function)"""
393
+ mgr = get_transaction_manager()
394
+ return mgr.create_transfer(from_address, to_address, amount, private_key, memo)
395
+
396
+ def send_transaction(transaction: Dict) -> Tuple[bool, str]:
397
+ """Send transaction (convenience function)"""
398
+ mgr = get_transaction_manager()
399
+ return mgr.send_transaction(transaction)