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.
- lunalib/__init__.py +19 -0
- lunalib/cli.py +18 -0
- lunalib/core/__init__.py +0 -0
- lunalib/core/blockchain.py +451 -0
- lunalib/core/crypto.py +32 -0
- lunalib/core/mempool.py +340 -0
- lunalib/core/wallet.py +635 -0
- lunalib/gtx/__init__.py +0 -0
- lunalib/gtx/bill_registry.py +122 -0
- lunalib/gtx/digital_bill.py +273 -0
- lunalib/gtx/genesis.py +349 -0
- lunalib/luna_lib.py +77 -0
- lunalib/mining/__init__.py +0 -0
- lunalib/mining/cuda_manager.py +137 -0
- lunalib/mining/difficulty.py +106 -0
- lunalib/mining/miner.py +107 -0
- lunalib/requirements.txt +44 -0
- lunalib/storage/__init__.py +0 -0
- lunalib/storage/cache.py +148 -0
- lunalib/storage/database.py +222 -0
- lunalib/storage/encryption.py +105 -0
- lunalib/transactions/__init__.py +0 -0
- lunalib/transactions/security.py +172 -0
- lunalib/transactions/transactions.py +583 -0
- lunalib/transactions/validator.py +71 -0
- lunalib-1.1.0.dist-info/METADATA +283 -0
- lunalib-1.1.0.dist-info/RECORD +30 -0
- lunalib-1.1.0.dist-info/WHEEL +5 -0
- lunalib-1.1.0.dist-info/entry_points.txt +2 -0
- lunalib-1.1.0.dist-info/top_level.txt +1 -0
lunalib/core/mempool.py
ADDED
|
@@ -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")
|