lunalib 1.1.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.
@@ -0,0 +1,340 @@
1
+ # lunalib/core/mempool.py - Updated version
2
+
3
+ import time
4
+ import requests
5
+ import threading
6
+ from queue import Queue
7
+ from typing import Dict, List, Optional, Set
8
+ import json
9
+ import hashlib
10
+
11
+ class MempoolManager:
12
+ """Manages transaction mempool and network broadcasting"""
13
+
14
+ def __init__(self, network_endpoints: List[str] = None):
15
+ self.network_endpoints = network_endpoints or ["https://bank.linglin.art"]
16
+ self.local_mempool = {} # {tx_hash: transaction}
17
+ self.pending_broadcasts = Queue()
18
+ self.confirmed_transactions: Set[str] = set()
19
+ self.max_mempool_size = 10000
20
+ self.broadcast_retries = 3
21
+ self.is_running = True
22
+
23
+ # Start background broadcast thread
24
+ self.broadcast_thread = threading.Thread(target=self._broadcast_worker, daemon=True)
25
+ self.broadcast_thread.start()
26
+
27
+ # Start cleanup thread
28
+ self.cleanup_thread = threading.Thread(target=self._cleanup_worker, daemon=True)
29
+ self.cleanup_thread.start()
30
+
31
+ def add_transaction(self, transaction: Dict) -> bool:
32
+ """Add transaction to local mempool and broadcast to network"""
33
+ try:
34
+ tx_hash = transaction.get('hash')
35
+ if not tx_hash:
36
+ print("DEBUG: Transaction missing hash")
37
+ return False
38
+
39
+ # Check if transaction already exists or is confirmed
40
+ if tx_hash in self.local_mempool or tx_hash in self.confirmed_transactions:
41
+ print(f"DEBUG: Transaction already processed: {tx_hash}")
42
+ return True
43
+
44
+ # Validate basic transaction structure
45
+ if not self._validate_transaction_basic(transaction):
46
+ print("DEBUG: Transaction validation failed")
47
+ return False
48
+
49
+ # Add to local mempool
50
+ self.local_mempool[tx_hash] = {
51
+ 'transaction': transaction,
52
+ 'timestamp': time.time(),
53
+ 'broadcast_attempts': 0,
54
+ 'last_broadcast': 0
55
+ }
56
+ print(f"DEBUG: Added transaction to mempool: {tx_hash}")
57
+
58
+ # Queue for broadcasting
59
+ self.pending_broadcasts.put(transaction)
60
+ print(f"DEBUG: Queued transaction for broadcasting: {tx_hash}")
61
+
62
+ return True
63
+
64
+ except Exception as e:
65
+ print(f"DEBUG: Error adding transaction to mempool: {e}")
66
+ return False
67
+
68
+ def broadcast_transaction(self, transaction: Dict) -> bool:
69
+ """Broadcast transaction to network endpoints - SIMPLIFIED FOR YOUR FLASK APP"""
70
+ tx_hash = transaction.get('hash')
71
+ print(f"DEBUG: Broadcasting transaction to mempool: {tx_hash}")
72
+
73
+ success = False
74
+ for endpoint in self.network_endpoints:
75
+ for attempt in range(self.broadcast_retries):
76
+ try:
77
+ # Use the correct endpoint for your Flask app
78
+ broadcast_endpoint = f"{endpoint}/mempool/add"
79
+
80
+ print(f"DEBUG: Attempt {attempt + 1} to {broadcast_endpoint}")
81
+ print(f"DEBUG: Transaction type: {transaction.get('type')}")
82
+ print(f"DEBUG: From: {transaction.get('from')}")
83
+ print(f"DEBUG: To: {transaction.get('to')}")
84
+ print(f"DEBUG: Amount: {transaction.get('amount')}")
85
+
86
+ headers = {
87
+ 'Content-Type': 'application/json',
88
+ 'User-Agent': 'LunaWallet/1.0'
89
+ }
90
+
91
+ # Send transaction directly to mempool endpoint
92
+ response = requests.post(
93
+ broadcast_endpoint,
94
+ json=transaction, # Send the transaction directly
95
+ headers=headers,
96
+ timeout=10
97
+ )
98
+
99
+ print(f"DEBUG: Response status: {response.status_code}")
100
+
101
+ if response.status_code in [200, 201]:
102
+ result = response.json()
103
+ print(f"DEBUG: Response data: {result}")
104
+
105
+ if result.get('success'):
106
+ print(f"✅ Successfully added to mempool via {broadcast_endpoint}")
107
+ success = True
108
+ break
109
+ else:
110
+ error_msg = result.get('error', 'Unknown error')
111
+ print(f"❌ Mempool rejected transaction: {error_msg}")
112
+ else:
113
+ print(f"❌ HTTP error {response.status_code}: {response.text}")
114
+
115
+ except requests.exceptions.ConnectionError:
116
+ print(f"❌ Cannot connect to {endpoint}")
117
+ except requests.exceptions.Timeout:
118
+ print(f"❌ Request timeout to {endpoint}")
119
+ except Exception as e:
120
+ print(f"❌ Exception during broadcast: {e}")
121
+
122
+ # Wait before retry
123
+ if attempt < self.broadcast_retries - 1:
124
+ print(f"DEBUG: Waiting before retry...")
125
+ time.sleep(2)
126
+
127
+ if success:
128
+ print(f"✅ Transaction {tx_hash} successfully broadcasted")
129
+ else:
130
+ print(f"❌ All broadcast attempts failed for transaction {tx_hash}")
131
+
132
+ return success
133
+
134
+ def test_connection(self) -> bool:
135
+ """Test connection to network endpoints"""
136
+ for endpoint in self.network_endpoints:
137
+ try:
138
+ print(f"DEBUG: Testing connection to {endpoint}")
139
+ # Test with a simple health check or mempool status
140
+ test_endpoints = [
141
+ f"{endpoint}/system/health",
142
+ f"{endpoint}/mempool/status",
143
+ f"{endpoint}/"
144
+ ]
145
+
146
+ for test_endpoint in test_endpoints:
147
+ try:
148
+ response = requests.get(test_endpoint, timeout=5)
149
+ print(f"DEBUG: Connection test response from {test_endpoint}: {response.status_code}")
150
+ if response.status_code == 200:
151
+ print(f"✅ Successfully connected to {endpoint}")
152
+ return True
153
+ except:
154
+ continue
155
+
156
+ except Exception as e:
157
+ print(f"DEBUG: Connection test failed for {endpoint}: {e}")
158
+
159
+ print("❌ All connection tests failed")
160
+ return False
161
+
162
+ def get_transaction(self, tx_hash: str) -> Optional[Dict]:
163
+ """Get transaction from mempool by hash"""
164
+ if tx_hash in self.local_mempool:
165
+ return self.local_mempool[tx_hash]['transaction']
166
+ return None
167
+
168
+ def get_pending_transactions(self, address: str = None) -> List[Dict]:
169
+ """Get all pending transactions, optionally filtered by address"""
170
+ transactions = []
171
+ for tx_data in self.local_mempool.values():
172
+ tx = tx_data['transaction']
173
+ if address is None or tx.get('from') == address or tx.get('to') == address:
174
+ transactions.append(tx)
175
+ return transactions
176
+
177
+ def remove_transaction(self, tx_hash: str):
178
+ """Remove transaction from mempool (usually after confirmation)"""
179
+ if tx_hash in self.local_mempool:
180
+ del self.local_mempool[tx_hash]
181
+ self.confirmed_transactions.add(tx_hash)
182
+ print(f"DEBUG: Removed transaction from mempool: {tx_hash}")
183
+
184
+ def is_transaction_pending(self, tx_hash: str) -> bool:
185
+ """Check if transaction is pending in mempool"""
186
+ return tx_hash in self.local_mempool
187
+
188
+ def is_transaction_confirmed(self, tx_hash: str) -> bool:
189
+ """Check if transaction has been confirmed"""
190
+ return tx_hash in self.confirmed_transactions
191
+
192
+ def get_mempool_size(self) -> int:
193
+ """Get current mempool size"""
194
+ return len(self.local_mempool)
195
+
196
+ def clear_mempool(self):
197
+ """Clear all transactions from mempool"""
198
+ self.local_mempool.clear()
199
+ print("DEBUG: Cleared mempool")
200
+
201
+ def _broadcast_worker(self):
202
+ """Background worker to broadcast pending transactions"""
203
+ while self.is_running:
204
+ try:
205
+ # Test connection first
206
+ if not self.test_connection():
207
+ print("DEBUG: No network connection, waiting...")
208
+ time.sleep(30)
209
+ continue
210
+
211
+ # Process all pending broadcasts
212
+ processed_count = 0
213
+ temporary_queue = Queue()
214
+
215
+ # Move all items to temporary queue to process
216
+ while not self.pending_broadcasts.empty():
217
+ temporary_queue.put(self.pending_broadcasts.get())
218
+
219
+ while not temporary_queue.empty() and processed_count < 10: # Limit per cycle
220
+ transaction = temporary_queue.get()
221
+ tx_hash = transaction.get('hash')
222
+
223
+ # Skip if already confirmed
224
+ if tx_hash in self.confirmed_transactions:
225
+ print(f"DEBUG: Transaction {tx_hash} already confirmed, skipping")
226
+ continue
227
+
228
+ # Update broadcast info
229
+ if tx_hash in self.local_mempool:
230
+ mempool_data = self.local_mempool[tx_hash]
231
+
232
+ # Check if we should stop trying
233
+ if mempool_data['broadcast_attempts'] >= self.broadcast_retries:
234
+ print(f"DEBUG: Max broadcast attempts reached for {tx_hash}, removing")
235
+ del self.local_mempool[tx_hash]
236
+ continue
237
+
238
+ mempool_data['broadcast_attempts'] += 1
239
+ mempool_data['last_broadcast'] = time.time()
240
+
241
+ # Broadcast transaction
242
+ success = self.broadcast_transaction(transaction)
243
+
244
+ if success:
245
+ print(f"✅ Broadcast successful for {tx_hash}")
246
+ # Transaction is in mempool, we can stop broadcasting it
247
+ else:
248
+ print(f"❌ Broadcast failed for {tx_hash}, attempt {mempool_data['broadcast_attempts']}")
249
+ # Re-queue for retry if under limit
250
+ if (tx_hash in self.local_mempool and
251
+ self.local_mempool[tx_hash]['broadcast_attempts'] < self.broadcast_retries):
252
+ self.pending_broadcasts.put(transaction)
253
+
254
+ processed_count += 1
255
+
256
+ # Sleep before next iteration
257
+ time.sleep(15)
258
+
259
+ except Exception as e:
260
+ print(f"DEBUG: Error in broadcast worker: {e}")
261
+ time.sleep(30)
262
+
263
+ def _cleanup_worker(self):
264
+ """Background worker to clean up old transactions"""
265
+ while self.is_running:
266
+ try:
267
+ current_time = time.time()
268
+ expired_txs = []
269
+
270
+ # Find transactions older than 1 hour or with too many failed attempts
271
+ for tx_hash, tx_data in self.local_mempool.items():
272
+ transaction_age = current_time - tx_data['timestamp']
273
+ if (transaction_age > 3600 or # 1 hour
274
+ tx_data['broadcast_attempts'] >= self.broadcast_retries * 2):
275
+ expired_txs.append(tx_hash)
276
+ print(f"DEBUG: Marking transaction as expired: {tx_hash}, age: {transaction_age:.0f}s, attempts: {tx_data['broadcast_attempts']}")
277
+
278
+ # Remove expired transactions
279
+ for tx_hash in expired_txs:
280
+ del self.local_mempool[tx_hash]
281
+ print(f"DEBUG: Removed expired/failed transaction: {tx_hash}")
282
+
283
+ # Clean up confirmed transactions set (keep only recent ones)
284
+ if len(self.confirmed_transactions) > self.max_mempool_size * 2:
285
+ # Convert to list and keep only recent half
286
+ confirmed_list = list(self.confirmed_transactions)
287
+ self.confirmed_transactions = set(confirmed_list[-self.max_mempool_size:])
288
+ print(f"DEBUG: Cleaned confirmed transactions, now {len(self.confirmed_transactions)} entries")
289
+
290
+ # Sleep for 5 minutes
291
+ time.sleep(300)
292
+
293
+ except Exception as e:
294
+ print(f"DEBUG: Error in cleanup worker: {e}")
295
+ time.sleep(60)
296
+
297
+ def _validate_transaction_basic(self, transaction: Dict) -> bool:
298
+ """Basic transaction validation"""
299
+ required_fields = ['type', 'from', 'to', 'amount', 'timestamp', 'hash']
300
+
301
+ for field in required_fields:
302
+ if field not in transaction:
303
+ print(f"DEBUG: Missing required field: {field}")
304
+ return False
305
+
306
+ # Validate amount
307
+ try:
308
+ amount = float(transaction['amount'])
309
+ if amount <= 0:
310
+ print("DEBUG: Invalid amount (must be positive)")
311
+ return False
312
+ except (ValueError, TypeError):
313
+ print("DEBUG: Invalid amount format")
314
+ return False
315
+
316
+ # Validate timestamp (not too far in future)
317
+ try:
318
+ timestamp = float(transaction['timestamp'])
319
+ if timestamp > time.time() + 300: # 5 minutes in future
320
+ print("DEBUG: Transaction timestamp too far in future")
321
+ return False
322
+ except (ValueError, TypeError):
323
+ print("DEBUG: Invalid timestamp format")
324
+ return False
325
+
326
+ # Validate addresses (basic format check)
327
+ from_addr = transaction.get('from', '')
328
+ to_addr = transaction.get('to', '')
329
+
330
+ if not from_addr or not to_addr:
331
+ print("DEBUG: Missing from or to address")
332
+ return False
333
+
334
+ print(f"✅ Transaction validation passed: {transaction.get('type')} from {from_addr} to {to_addr}")
335
+ return True
336
+
337
+ def stop(self):
338
+ """Stop the mempool manager"""
339
+ self.is_running = False
340
+ print("DEBUG: Mempool manager stopped")