Fred808 commited on
Commit
5879890
·
verified ·
1 Parent(s): 1c118b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +406 -411
app.py CHANGED
@@ -1,411 +1,406 @@
1
- import os
2
- import json
3
- import time
4
- import threading
5
- from fastapi import FastAPI, HTTPException, BackgroundTasks
6
- from fastapi.middleware.cors import CORSMiddleware
7
- from fastapi.responses import JSONResponse
8
- import uvicorn
9
- from typing import Dict
10
- from datetime import datetime
11
-
12
- # Import from parallel_miner_v3
13
- from parallel_miner_v3 import ParallelMiner, MiningCore, HashUnit
14
-
15
- # FastAPI App Definition
16
- app = FastAPI(
17
- title="Bitcoin Mining API",
18
- description="API endpoints for Bitcoin mining operations using electron-speed SHA-256",
19
- version="3.0.0"
20
- )
21
-
22
- # Add CORS middleware to allow cross-origin requests
23
- app.add_middleware(
24
- CORSMiddleware,
25
- allow_origins=["*"], # Allows all origins
26
- allow_credentials=True,
27
- allow_methods=["*"], # Allows all methods
28
- allow_headers=["*"],
29
- )
30
-
31
- # Stats file path
32
- STATS_FILE = "mining_stats_history.json"
33
-
34
- # Global variables to track mining status
35
- miner_instance = None
36
- mining_thread = None
37
- mining_stats = {
38
- "is_mining": False,
39
- "total_hashes": 0,
40
- "blocks_found": 0,
41
- "hash_rate": 0.0,
42
- "best_hash": None,
43
- "best_hash_difficulty": 0,
44
- "start_time": None,
45
- "total_runtime": 0, # Cumulative runtime across all sessions
46
- "session_count": 0,
47
- "best_session_hashrate": 0,
48
- "all_time_total_hashes": 0,
49
- "logs": []
50
- }
51
-
52
- def save_mining_stats():
53
- """Save mining statistics to file"""
54
- try:
55
- if os.path.exists(STATS_FILE):
56
- with open(STATS_FILE, 'r') as f:
57
- historical_stats = json.load(f)
58
- else:
59
- historical_stats = {"sessions": []}
60
-
61
- # Calculate final stats for this session
62
- end_time = time.time()
63
- elapsed = end_time - mining_stats["start_time"] if mining_stats["start_time"] else 0
64
- hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
65
-
66
- session_stats = {
67
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
68
- "duration": f"{elapsed:.2f}s",
69
- "total_hashes": mining_stats["total_hashes"],
70
- "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s",
71
- "blocks_found": mining_stats["blocks_found"],
72
- "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None,
73
- "best_hash_difficulty": mining_stats["best_hash_difficulty"]
74
- }
75
-
76
- historical_stats["sessions"].append(session_stats)
77
- historical_stats["total_runtime"] = mining_stats["total_runtime"] + elapsed
78
- historical_stats["total_hashes"] = mining_stats["all_time_total_hashes"] + mining_stats["total_hashes"]
79
- historical_stats["total_blocks"] = sum(s["blocks_found"] for s in historical_stats["sessions"])
80
- historical_stats["best_session_hashrate"] = max(
81
- mining_stats["best_session_hashrate"],
82
- hash_rate/1000 # Convert to KH/s
83
- )
84
-
85
- with open(STATS_FILE, 'w') as f:
86
- json.dump(historical_stats, f, indent=2)
87
-
88
- except Exception as e:
89
- log_mining(f"Error saving mining stats: {str(e)}")
90
-
91
- def log_mining(message):
92
- """Add a mining log message with timestamp"""
93
- timestamp = datetime.now().strftime("%H:%M:%S")
94
- log_entry = f"[{timestamp}] {message}"
95
- mining_stats["logs"].append(log_entry)
96
-
97
- # Keep only the last 100 logs
98
- if len(mining_stats["logs"]) > 100:
99
- mining_stats["logs"] = mining_stats["logs"][-100:]
100
-
101
- print(log_entry)
102
-
103
- @app.on_event("startup")
104
- async def startup_event():
105
- """Initialize mining components and start mining on startup"""
106
- global miner_instance, mining_thread, mining_stats
107
- if not miner_instance:
108
- log_mining("🚀 Initializing Bitcoin mining components...")
109
- miner_instance = ParallelMiner(num_cores=5) # Increased cores for better performance
110
-
111
- # Initialize mining stats
112
- mining_stats["is_mining"] = True
113
- mining_stats["start_time"] = time.time()
114
- mining_stats["total_hashes"] = 0
115
- mining_stats["blocks_found"] = 0
116
- mining_stats["best_hash"] = None
117
-
118
- # Start mining in background thread
119
- mining_thread = threading.Thread(
120
- target=miner_instance.start_mining,
121
- kwargs={"duration": None} # Run forever
122
- )
123
- mining_thread.daemon = True
124
- mining_thread.start()
125
-
126
- log_mining(f"Started mining automatically with {len(miner_instance.cores)} cores")
127
-
128
- from fastapi.staticfiles import StaticFiles
129
-
130
- # Serve static files
131
- app.mount("/static", StaticFiles(directory="static"), name="static")
132
-
133
- @app.get("/")
134
- async def root():
135
- return FileResponse("static/index.html")
136
-
137
-
138
-
139
- @app.get("/mining/status")
140
- async def get_mining_status():
141
- """Get current mining status and statistics"""
142
- if not miner_instance:
143
- return {"error": "Mining system not initialized"}
144
-
145
- current_time = time.time()
146
- if mining_stats["start_time"]:
147
- elapsed = current_time - mining_stats["start_time"]
148
- else:
149
- elapsed = 0
150
-
151
- # Update mining stats from miner instance
152
- mining_stats["total_hashes"] = miner_instance.total_hashes
153
- mining_stats["blocks_found"] = miner_instance.blocks_found
154
- mining_stats["best_hash"] = miner_instance.best_hash
155
- mining_stats["best_hash_difficulty"] = miner_instance.best_hash_difficulty
156
-
157
- return {
158
- "is_mining": mining_stats["is_mining"],
159
- "total_hashes": miner_instance.total_hashes,
160
- "hash_rate": f"{miner_instance.current_hashrate/1000:.2f} KH/s",
161
- "blocks_found": miner_instance.blocks_found,
162
- "best_hash": miner_instance.best_hash.hex() if miner_instance.best_hash else None,
163
- "difficulty": {
164
- "network": miner_instance.network_difficulty,
165
- "best_achieved": miner_instance.best_hash_difficulty,
166
- "percent_to_network": f"{(miner_instance.best_hash_difficulty / miner_instance.network_difficulty * 100):.4f}%" if miner_instance.network_difficulty > 0 else "0%"
167
- },
168
- "uptime": f"{elapsed:.2f}s" if mining_stats["start_time"] else "0s",
169
- "cores_active": len(miner_instance.cores) if miner_instance else 0,
170
- "units_per_core": len(miner_instance.cores[0].units) if miner_instance and miner_instance.cores else 0,
171
- "logs": mining_stats["logs"][-10:] # Last 10 logs
172
- }
173
-
174
- @app.get("/mining/performance")
175
- async def get_mining_performance():
176
- """Get detailed mining performance metrics"""
177
- global miner_instance, mining_stats
178
-
179
- if not miner_instance:
180
- return {"error": "Mining system not initialized"}
181
-
182
- current_time = time.time()
183
- if mining_stats["start_time"]:
184
- elapsed = current_time - mining_stats["start_time"]
185
- hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
186
- hashes_per_core = mining_stats["total_hashes"] / len(miner_instance.cores) if miner_instance.cores else 0
187
- else:
188
- elapsed = 0
189
- hash_rate = 0
190
- hashes_per_core = 0
191
-
192
- core_stats = []
193
- if miner_instance and miner_instance.cores:
194
- for i, core in enumerate(miner_instance.cores):
195
- core_stats.append({
196
- "core_id": i,
197
- "active_units": len(core.units),
198
- "status": "active" if mining_stats["is_mining"] else "idle"
199
- })
200
-
201
- return {
202
- "overall_performance": {
203
- "hash_rate": f"{miner_instance.current_hashrate/1000:.2f} KH/s",
204
- "total_hashes": miner_instance.total_hashes,
205
- "blocks_found": miner_instance.blocks_found,
206
- "uptime": f"{elapsed:.2f}s",
207
- "hashes_per_core": f"{(miner_instance.total_hashes/len(miner_instance.cores))/1000:.2f}K"
208
- },
209
- "core_utilization": {
210
- "total_cores": len(miner_instance.cores),
211
- "active_cores": len([c for c in core_stats if c["status"] == "active"]),
212
- "cores": core_stats
213
- },
214
- "memory_usage": {
215
- "core_memory": len(miner_instance.cores) * 1024 * 1024, # Approximate memory usage per core
216
- "total_allocated": len(miner_instance.cores) * len(miner_instance.cores[0].units) * 1024 if miner_instance.cores else 0
217
- }
218
- }
219
-
220
- from fastapi.encoders import jsonable_encoder
221
-
222
-
223
- @app.post("/mining/start")
224
- async def start_mining():
225
- """Start Bitcoin mining operations if not already running"""
226
- global mining_thread, miner_instance, mining_stats
227
-
228
- if mining_stats["is_mining"]:
229
- return {"message": "Mining is already running", "status": "already_running"}
230
-
231
- if not miner_instance:
232
- miner_instance = ParallelMiner()
233
-
234
- # Reset statistics
235
- mining_stats["is_mining"] = True
236
- mining_stats["start_time"] = time.time()
237
- mining_stats["total_hashes"] = 0
238
- mining_stats["blocks_found"] = 0
239
- mining_stats["best_hash"] = None
240
-
241
- # Start mining in background thread
242
- mining_thread = threading.Thread(
243
- target=miner_instance.start_mining,
244
- kwargs={"duration": None} # Always run indefinitely
245
- )
246
- mining_thread.daemon = True
247
- mining_thread.start()
248
-
249
- log_mining(f"Started mining with {len(miner_instance.cores)} cores")
250
-
251
- return {
252
- "message": "Mining started successfully",
253
- "status": "started",
254
- "config": {
255
- "cores": len(miner_instance.cores),
256
- "units_per_core": len(miner_instance.cores[0].units),
257
- "duration": "indefinite",
258
- }
259
- }
260
-
261
- @app.post("/mining/stop")
262
- async def stop_mining():
263
- """Stop Bitcoin mining operations"""
264
- global miner_instance, mining_stats
265
-
266
- if not mining_stats["is_mining"]:
267
- return {"message": "Mining is not currently running", "status": "not_running"}
268
-
269
- if miner_instance:
270
- miner_instance.mining = False
271
- mining_stats["is_mining"] = False
272
-
273
- # Calculate final statistics
274
- end_time = time.time()
275
- elapsed = end_time - mining_stats["start_time"]
276
- hash_rate = miner_instance.current_hashrate
277
-
278
- # Update cumulative statistics
279
- mining_stats["total_runtime"] += elapsed
280
- mining_stats["all_time_total_hashes"] += miner_instance.total_hashes
281
- mining_stats["best_session_hashrate"] = max(
282
- mining_stats["best_session_hashrate"],
283
- hash_rate/1000 # KH/s
284
- )
285
-
286
- # Get final stats from miner
287
- mining_stats["total_hashes"] = miner_instance.total_hashes
288
- mining_stats["blocks_found"] = miner_instance.blocks_found
289
- mining_stats["best_hash"] = miner_instance.best_hash
290
- mining_stats["best_hash_difficulty"] = miner_instance.best_hash_difficulty
291
-
292
- # Save stats to file
293
- save_mining_stats()
294
-
295
- # Log comprehensive statistics
296
- log_mining("=== Mining Session Completed ===")
297
- log_mining(f"Session Duration: {elapsed:.2f}s")
298
- log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}")
299
- log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s")
300
- log_mining(f"Blocks Found: {mining_stats['blocks_found']}")
301
- log_mining(f"Best Hash: {mining_stats['best_hash'].hex() if mining_stats['best_hash'] else 'None'}")
302
- log_mining(f"Best Hash Difficulty: {mining_stats['best_hash_difficulty']}")
303
- log_mining("\n=== All-Time Statistics ===")
304
- log_mining(f"Total Runtime: {mining_stats['total_runtime']:.2f}s")
305
- log_mining(f"Total Hashes: {mining_stats['all_time_total_hashes']:,}")
306
- log_mining(f"Total Blocks Found: {mining_stats['blocks_found']}")
307
- log_mining(f"Best Session Hash Rate: {mining_stats['best_session_hashrate']:.2f} KH/s")
308
-
309
- return {
310
- "message": "Mining stopped successfully",
311
- "status": "stopped",
312
- "session_stats": {
313
- "duration": f"{elapsed:.2f}s",
314
- "total_hashes": mining_stats["total_hashes"],
315
- "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s",
316
- "blocks_found": mining_stats["blocks_found"],
317
- "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None,
318
- "best_hash_difficulty": mining_stats["best_hash_difficulty"]
319
- },
320
- "all_time_stats": {
321
- "total_runtime": f"{mining_stats['total_runtime']:.2f}s",
322
- "total_hashes": mining_stats["all_time_total_hashes"],
323
- "total_blocks": mining_stats["blocks_found"],
324
- "best_session_hashrate": f"{mining_stats['best_session_hashrate']:.2f} KH/s"
325
- }
326
- }
327
-
328
- return {"message": "Mining instance not found", "status": "error"}
329
-
330
-
331
- @app.get("/mining/history")
332
- async def get_mining_history():
333
- """Get historical mining statistics"""
334
- try:
335
- if os.path.exists(STATS_FILE):
336
- with open(STATS_FILE, 'r') as f:
337
- historical_stats = json.load(f)
338
- return historical_stats
339
- else:
340
- return {
341
- "sessions": [],
342
- "total_runtime": 0,
343
- "total_hashes": 0,
344
- "total_blocks": 0,
345
- "best_session_hashrate": 0
346
- }
347
- except Exception as e:
348
- raise HTTPException(status_code=500, detail=f"Error reading mining history: {str(e)}")
349
-
350
- def handle_shutdown():
351
- """Handle graceful shutdown and save statistics"""
352
- if mining_stats["is_mining"] and miner_instance:
353
- log_mining("\n🛑 Server shutdown detected - Saving final mining statistics...")
354
- miner_instance.mining = False
355
- mining_stats["is_mining"] = False
356
-
357
- # Calculate and save final statistics
358
- end_time = time.time()
359
- elapsed = end_time - mining_stats["start_time"]
360
- hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
361
-
362
- # Update cumulative statistics
363
- mining_stats["total_runtime"] += elapsed
364
- mining_stats["all_time_total_hashes"] += mining_stats["total_hashes"]
365
- mining_stats["best_session_hashrate"] = max(
366
- mining_stats["best_session_hashrate"],
367
- hash_rate/1000
368
- )
369
-
370
- # Log final stats before shutdown
371
- log_mining("=== Final Mining Statistics ===")
372
- log_mining(f"Session Duration: {elapsed:.2f}s")
373
- log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}")
374
- log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s")
375
- log_mining(f"Blocks Found: {mining_stats['blocks_found']}")
376
- if mining_stats["best_hash"]:
377
- log_mining(f"Best Hash: {mining_stats['best_hash'].hex()}")
378
-
379
- # Save stats to file
380
- save_mining_stats()
381
- log_mining("Statistics saved successfully")
382
- log_mining("Server shutting down... Goodbye! 👋")
383
-
384
- if __name__ == "__main__":
385
- # Start the FastAPI server
386
- print("Starting Bitcoin Mining API Server...")
387
- print("API Documentation will be available at: http://localhost:8000/docs")
388
- print("API Root endpoint: http://localhost:8000/")
389
- print("\nEndpoints:")
390
- print(" - GET /mining/status - Current mining status")
391
- print(" - GET /mining/performance - Detailed performance metrics")
392
- print(" - GET /mining/history - Historical mining statistics")
393
- print(" - POST /mining/start - Start mining (if stopped)")
394
- print(" - POST /mining/stop - Stop mining and show stats")
395
- print("\nPress Ctrl+C to stop mining and save statistics")
396
-
397
- try:
398
- uvicorn.run(
399
- app,
400
- host="0.0.0.0",
401
- port=8000,
402
- log_level="info",
403
- reload=False # Set to False for production
404
- )
405
- except KeyboardInterrupt:
406
- handle_shutdown()
407
- except Exception as e:
408
- log_mining(f"Error during server operation: {str(e)}")
409
- handle_shutdown()
410
- raise
411
-
 
1
+ import os
2
+ import json
3
+ import time
4
+ import threading
5
+ from fastapi import FastAPI, HTTPException, BackgroundTasks
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+ from fastapi.responses import JSONResponse
8
+ import uvicorn
9
+ from typing import Dict
10
+ from datetime import datetime
11
+
12
+ # Import from parallel_miner_v3
13
+ from parallel_miner_v3 import ParallelMiner, MiningCore, HashUnit
14
+
15
+ # FastAPI App Definition
16
+ app = FastAPI(
17
+ title="Bitcoin Mining API",
18
+ description="API endpoints for Bitcoin mining operations using electron-speed SHA-256",
19
+ version="3.0.0"
20
+ )
21
+
22
+ # Add CORS middleware to allow cross-origin requests
23
+ app.add_middleware(
24
+ CORSMiddleware,
25
+ allow_origins=["*"], # Allows all origins
26
+ allow_credentials=True,
27
+ allow_methods=["*"], # Allows all methods
28
+ allow_headers=["*"],
29
+ )
30
+
31
+ # Stats file path
32
+ STATS_FILE = "mining_stats_history.json"
33
+
34
+ # Global variables to track mining status
35
+ miner_instance = None
36
+ mining_thread = None
37
+ mining_stats = {
38
+ "is_mining": False,
39
+ "total_hashes": 0,
40
+ "blocks_found": 0,
41
+ "hash_rate": 0.0,
42
+ "best_hash": None,
43
+ "best_hash_difficulty": 0,
44
+ "start_time": None,
45
+ "total_runtime": 0, # Cumulative runtime across all sessions
46
+ "session_count": 0,
47
+ "best_session_hashrate": 0,
48
+ "all_time_total_hashes": 0,
49
+ "logs": []
50
+ }
51
+
52
+ def save_mining_stats():
53
+ """Save mining statistics to file"""
54
+ try:
55
+ if os.path.exists(STATS_FILE):
56
+ with open(STATS_FILE, 'r') as f:
57
+ historical_stats = json.load(f)
58
+ else:
59
+ historical_stats = {"sessions": []}
60
+
61
+ # Calculate final stats for this session
62
+ end_time = time.time()
63
+ elapsed = end_time - mining_stats["start_time"] if mining_stats["start_time"] else 0
64
+ hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
65
+
66
+ session_stats = {
67
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
68
+ "duration": f"{elapsed:.2f}s",
69
+ "total_hashes": mining_stats["total_hashes"],
70
+ "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s",
71
+ "blocks_found": mining_stats["blocks_found"],
72
+ "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None,
73
+ "best_hash_difficulty": mining_stats["best_hash_difficulty"]
74
+ }
75
+
76
+ historical_stats["sessions"].append(session_stats)
77
+ historical_stats["total_runtime"] = mining_stats["total_runtime"] + elapsed
78
+ historical_stats["total_hashes"] = mining_stats["all_time_total_hashes"] + mining_stats["total_hashes"]
79
+ historical_stats["total_blocks"] = sum(s["blocks_found"] for s in historical_stats["sessions"])
80
+ historical_stats["best_session_hashrate"] = max(
81
+ mining_stats["best_session_hashrate"],
82
+ hash_rate/1000 # Convert to KH/s
83
+ )
84
+
85
+ with open(STATS_FILE, 'w') as f:
86
+ json.dump(historical_stats, f, indent=2)
87
+
88
+ except Exception as e:
89
+ log_mining(f"Error saving mining stats: {str(e)}")
90
+
91
+ def log_mining(message):
92
+ """Add a mining log message with timestamp"""
93
+ timestamp = datetime.now().strftime("%H:%M:%S")
94
+ log_entry = f"[{timestamp}] {message}"
95
+ mining_stats["logs"].append(log_entry)
96
+
97
+ # Keep only the last 100 logs
98
+ if len(mining_stats["logs"]) > 100:
99
+ mining_stats["logs"] = mining_stats["logs"][-100:]
100
+
101
+ print(log_entry)
102
+
103
+ @app.on_event("startup")
104
+ async def startup_event():
105
+ """Initialize mining components and start mining on startup"""
106
+ global miner_instance, mining_thread, mining_stats
107
+ if not miner_instance:
108
+ log_mining("�� Initializing Bitcoin mining components...")
109
+ miner_instance = ParallelMiner(num_cores=5) # Increased cores for better performance
110
+
111
+ # Initialize mining stats
112
+ mining_stats["is_mining"] = True
113
+ mining_stats["start_time"] = time.time()
114
+ mining_stats["total_hashes"] = 0
115
+ mining_stats["blocks_found"] = 0
116
+ mining_stats["best_hash"] = None
117
+
118
+ # Start mining in background thread
119
+ mining_thread = threading.Thread(
120
+ target=miner_instance.start_mining,
121
+ kwargs={"duration": None} # Run forever
122
+ )
123
+ mining_thread.daemon = True
124
+ mining_thread.start()
125
+
126
+ log_mining(f"Started mining automatically with {len(miner_instance.cores)} cores")
127
+
128
+ from fastapi.staticfiles import StaticFiles
129
+
130
+ # Serve static files
131
+
132
+
133
+
134
+ @app.get("/mining/status")
135
+ async def get_mining_status():
136
+ """Get current mining status and statistics"""
137
+ if not miner_instance:
138
+ return {"error": "Mining system not initialized"}
139
+
140
+ current_time = time.time()
141
+ if mining_stats["start_time"]:
142
+ elapsed = current_time - mining_stats["start_time"]
143
+ else:
144
+ elapsed = 0
145
+
146
+ # Update mining stats from miner instance
147
+ mining_stats["total_hashes"] = miner_instance.total_hashes
148
+ mining_stats["blocks_found"] = miner_instance.blocks_found
149
+ mining_stats["best_hash"] = miner_instance.best_hash
150
+ mining_stats["best_hash_difficulty"] = miner_instance.best_hash_difficulty
151
+
152
+ return {
153
+ "is_mining": mining_stats["is_mining"],
154
+ "total_hashes": miner_instance.total_hashes,
155
+ "hash_rate": f"{miner_instance.current_hashrate/1000:.2f} KH/s",
156
+ "blocks_found": miner_instance.blocks_found,
157
+ "best_hash": miner_instance.best_hash.hex() if miner_instance.best_hash else None,
158
+ "difficulty": {
159
+ "network": miner_instance.network_difficulty,
160
+ "best_achieved": miner_instance.best_hash_difficulty,
161
+ "percent_to_network": f"{(miner_instance.best_hash_difficulty / miner_instance.network_difficulty * 100):.4f}%" if miner_instance.network_difficulty > 0 else "0%"
162
+ },
163
+ "uptime": f"{elapsed:.2f}s" if mining_stats["start_time"] else "0s",
164
+ "cores_active": len(miner_instance.cores) if miner_instance else 0,
165
+ "units_per_core": len(miner_instance.cores[0].units) if miner_instance and miner_instance.cores else 0,
166
+ "logs": mining_stats["logs"][-10:] # Last 10 logs
167
+ }
168
+
169
+ @app.get("/mining/performance")
170
+ async def get_mining_performance():
171
+ """Get detailed mining performance metrics"""
172
+ global miner_instance, mining_stats
173
+
174
+ if not miner_instance:
175
+ return {"error": "Mining system not initialized"}
176
+
177
+ current_time = time.time()
178
+ if mining_stats["start_time"]:
179
+ elapsed = current_time - mining_stats["start_time"]
180
+ hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
181
+ hashes_per_core = mining_stats["total_hashes"] / len(miner_instance.cores) if miner_instance.cores else 0
182
+ else:
183
+ elapsed = 0
184
+ hash_rate = 0
185
+ hashes_per_core = 0
186
+
187
+ core_stats = []
188
+ if miner_instance and miner_instance.cores:
189
+ for i, core in enumerate(miner_instance.cores):
190
+ core_stats.append({
191
+ "core_id": i,
192
+ "active_units": len(core.units),
193
+ "status": "active" if mining_stats["is_mining"] else "idle"
194
+ })
195
+
196
+ return {
197
+ "overall_performance": {
198
+ "hash_rate": f"{miner_instance.current_hashrate/1000:.2f} KH/s",
199
+ "total_hashes": miner_instance.total_hashes,
200
+ "blocks_found": miner_instance.blocks_found,
201
+ "uptime": f"{elapsed:.2f}s",
202
+ "hashes_per_core": f"{(miner_instance.total_hashes/len(miner_instance.cores))/1000:.2f}K"
203
+ },
204
+ "core_utilization": {
205
+ "total_cores": len(miner_instance.cores),
206
+ "active_cores": len([c for c in core_stats if c["status"] == "active"]),
207
+ "cores": core_stats
208
+ },
209
+ "memory_usage": {
210
+ "core_memory": len(miner_instance.cores) * 1024 * 1024, # Approximate memory usage per core
211
+ "total_allocated": len(miner_instance.cores) * len(miner_instance.cores[0].units) * 1024 if miner_instance.cores else 0
212
+ }
213
+ }
214
+
215
+ from fastapi.encoders import jsonable_encoder
216
+
217
+
218
+ @app.post("/mining/start")
219
+ async def start_mining():
220
+ """Start Bitcoin mining operations if not already running"""
221
+ global mining_thread, miner_instance, mining_stats
222
+
223
+ if mining_stats["is_mining"]:
224
+ return {"message": "Mining is already running", "status": "already_running"}
225
+
226
+ if not miner_instance:
227
+ miner_instance = ParallelMiner()
228
+
229
+ # Reset statistics
230
+ mining_stats["is_mining"] = True
231
+ mining_stats["start_time"] = time.time()
232
+ mining_stats["total_hashes"] = 0
233
+ mining_stats["blocks_found"] = 0
234
+ mining_stats["best_hash"] = None
235
+
236
+ # Start mining in background thread
237
+ mining_thread = threading.Thread(
238
+ target=miner_instance.start_mining,
239
+ kwargs={"duration": None} # Always run indefinitely
240
+ )
241
+ mining_thread.daemon = True
242
+ mining_thread.start()
243
+
244
+ log_mining(f"Started mining with {len(miner_instance.cores)} cores")
245
+
246
+ return {
247
+ "message": "Mining started successfully",
248
+ "status": "started",
249
+ "config": {
250
+ "cores": len(miner_instance.cores),
251
+ "units_per_core": len(miner_instance.cores[0].units),
252
+ "duration": "indefinite",
253
+ }
254
+ }
255
+
256
+ @app.post("/mining/stop")
257
+ async def stop_mining():
258
+ """Stop Bitcoin mining operations"""
259
+ global miner_instance, mining_stats
260
+
261
+ if not mining_stats["is_mining"]:
262
+ return {"message": "Mining is not currently running", "status": "not_running"}
263
+
264
+ if miner_instance:
265
+ miner_instance.mining = False
266
+ mining_stats["is_mining"] = False
267
+
268
+ # Calculate final statistics
269
+ end_time = time.time()
270
+ elapsed = end_time - mining_stats["start_time"]
271
+ hash_rate = miner_instance.current_hashrate
272
+
273
+ # Update cumulative statistics
274
+ mining_stats["total_runtime"] += elapsed
275
+ mining_stats["all_time_total_hashes"] += miner_instance.total_hashes
276
+ mining_stats["best_session_hashrate"] = max(
277
+ mining_stats["best_session_hashrate"],
278
+ hash_rate/1000 # KH/s
279
+ )
280
+
281
+ # Get final stats from miner
282
+ mining_stats["total_hashes"] = miner_instance.total_hashes
283
+ mining_stats["blocks_found"] = miner_instance.blocks_found
284
+ mining_stats["best_hash"] = miner_instance.best_hash
285
+ mining_stats["best_hash_difficulty"] = miner_instance.best_hash_difficulty
286
+
287
+ # Save stats to file
288
+ save_mining_stats()
289
+
290
+ # Log comprehensive statistics
291
+ log_mining("=== Mining Session Completed ===")
292
+ log_mining(f"Session Duration: {elapsed:.2f}s")
293
+ log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}")
294
+ log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s")
295
+ log_mining(f"Blocks Found: {mining_stats['blocks_found']}")
296
+ log_mining(f"Best Hash: {mining_stats['best_hash'].hex() if mining_stats['best_hash'] else 'None'}")
297
+ log_mining(f"Best Hash Difficulty: {mining_stats['best_hash_difficulty']}")
298
+ log_mining("\n=== All-Time Statistics ===")
299
+ log_mining(f"Total Runtime: {mining_stats['total_runtime']:.2f}s")
300
+ log_mining(f"Total Hashes: {mining_stats['all_time_total_hashes']:,}")
301
+ log_mining(f"Total Blocks Found: {mining_stats['blocks_found']}")
302
+ log_mining(f"Best Session Hash Rate: {mining_stats['best_session_hashrate']:.2f} KH/s")
303
+
304
+ return {
305
+ "message": "Mining stopped successfully",
306
+ "status": "stopped",
307
+ "session_stats": {
308
+ "duration": f"{elapsed:.2f}s",
309
+ "total_hashes": mining_stats["total_hashes"],
310
+ "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s",
311
+ "blocks_found": mining_stats["blocks_found"],
312
+ "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None,
313
+ "best_hash_difficulty": mining_stats["best_hash_difficulty"]
314
+ },
315
+ "all_time_stats": {
316
+ "total_runtime": f"{mining_stats['total_runtime']:.2f}s",
317
+ "total_hashes": mining_stats["all_time_total_hashes"],
318
+ "total_blocks": mining_stats["blocks_found"],
319
+ "best_session_hashrate": f"{mining_stats['best_session_hashrate']:.2f} KH/s"
320
+ }
321
+ }
322
+
323
+ return {"message": "Mining instance not found", "status": "error"}
324
+
325
+
326
+ @app.get("/mining/history")
327
+ async def get_mining_history():
328
+ """Get historical mining statistics"""
329
+ try:
330
+ if os.path.exists(STATS_FILE):
331
+ with open(STATS_FILE, 'r') as f:
332
+ historical_stats = json.load(f)
333
+ return historical_stats
334
+ else:
335
+ return {
336
+ "sessions": [],
337
+ "total_runtime": 0,
338
+ "total_hashes": 0,
339
+ "total_blocks": 0,
340
+ "best_session_hashrate": 0
341
+ }
342
+ except Exception as e:
343
+ raise HTTPException(status_code=500, detail=f"Error reading mining history: {str(e)}")
344
+
345
+ def handle_shutdown():
346
+ """Handle graceful shutdown and save statistics"""
347
+ if mining_stats["is_mining"] and miner_instance:
348
+ log_mining("\n🛑 Server shutdown detected - Saving final mining statistics...")
349
+ miner_instance.mining = False
350
+ mining_stats["is_mining"] = False
351
+
352
+ # Calculate and save final statistics
353
+ end_time = time.time()
354
+ elapsed = end_time - mining_stats["start_time"]
355
+ hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0
356
+
357
+ # Update cumulative statistics
358
+ mining_stats["total_runtime"] += elapsed
359
+ mining_stats["all_time_total_hashes"] += mining_stats["total_hashes"]
360
+ mining_stats["best_session_hashrate"] = max(
361
+ mining_stats["best_session_hashrate"],
362
+ hash_rate/1000
363
+ )
364
+
365
+ # Log final stats before shutdown
366
+ log_mining("=== Final Mining Statistics ===")
367
+ log_mining(f"Session Duration: {elapsed:.2f}s")
368
+ log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}")
369
+ log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s")
370
+ log_mining(f"Blocks Found: {mining_stats['blocks_found']}")
371
+ if mining_stats["best_hash"]:
372
+ log_mining(f"Best Hash: {mining_stats['best_hash'].hex()}")
373
+
374
+ # Save stats to file
375
+ save_mining_stats()
376
+ log_mining("Statistics saved successfully")
377
+ log_mining("Server shutting down... Goodbye! 👋")
378
+
379
+ if __name__ == "__main__":
380
+ # Start the FastAPI server
381
+ print("Starting Bitcoin Mining API Server...")
382
+ print("API Documentation will be available at: http://localhost:8000/docs")
383
+ print("API Root endpoint: http://localhost:8000/")
384
+ print("\nEndpoints:")
385
+ print(" - GET /mining/status - Current mining status")
386
+ print(" - GET /mining/performance - Detailed performance metrics")
387
+ print(" - GET /mining/history - Historical mining statistics")
388
+ print(" - POST /mining/start - Start mining (if stopped)")
389
+ print(" - POST /mining/stop - Stop mining and show stats")
390
+ print("\nPress Ctrl+C to stop mining and save statistics")
391
+
392
+ try:
393
+ uvicorn.run(
394
+ app,
395
+ host="0.0.0.0",
396
+ port=8000,
397
+ log_level="info",
398
+ reload=False # Set to False for production
399
+ )
400
+ except KeyboardInterrupt:
401
+ handle_shutdown()
402
+ except Exception as e:
403
+ log_mining(f"Error during server operation: {str(e)}")
404
+ handle_shutdown()
405
+ raise
406
+