Download parallel_miner_v2.py from Fred808/NBA: direct link, hf CLI and curl.
- Browser
- Download file 15.6 kB
-
https://huggingface.co/spaces/Fred808/NBA/resolve/main/parallel_miner_v2.py
- Command line
-
hf download hf://spaces/Fred808/NBA/parallel_miner_v2.py
-
curl -L -o parallel_miner_v2.py https://huggingface.co/spaces/Fred808/NBA/resolve/main/parallel_miner_v2.py
15.6 kB
| import os | |
| import json | |
| import time | |
| import threading | |
| from fastapi import FastAPI, HTTPException, BackgroundTasks | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| import uvicorn | |
| from typing import Dict | |
| from datetime import datetime | |
| # Import from parallel_miner_v3 | |
| from parallel_miner_v3 import ParallelMiner, MiningCore, HashUnit | |
| # FastAPI App Definition | |
| app = FastAPI( | |
| title="Bitcoin Mining API", | |
| description="API endpoints for Bitcoin mining operations using electron-speed SHA-256", | |
| version="3.0.0" | |
| ) | |
| # Add CORS middleware to allow cross-origin requests | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # Allows all origins | |
| allow_credentials=True, | |
| allow_methods=["*"], # Allows all methods | |
| allow_headers=["*"], | |
| ) | |
| # Stats file path | |
| STATS_FILE = "mining_stats_history.json" | |
| # Global variables to track mining status | |
| miner_instance = None | |
| mining_thread = None | |
| mining_stats = { | |
| "is_mining": False, | |
| "total_hashes": 0, | |
| "blocks_found": 0, | |
| "hash_rate": 0.0, | |
| "best_hash": None, | |
| "best_hash_difficulty": 0, | |
| "start_time": None, | |
| "total_runtime": 0, # Cumulative runtime across all sessions | |
| "session_count": 0, | |
| "best_session_hashrate": 0, | |
| "all_time_total_hashes": 0, | |
| "logs": [] | |
| } | |
| def save_mining_stats(): | |
| """Save mining statistics to file""" | |
| try: | |
| if os.path.exists(STATS_FILE): | |
| with open(STATS_FILE, 'r') as f: | |
| historical_stats = json.load(f) | |
| else: | |
| historical_stats = {"sessions": []} | |
| # Calculate final stats for this session | |
| end_time = time.time() | |
| elapsed = end_time - mining_stats["start_time"] if mining_stats["start_time"] else 0 | |
| hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0 | |
| session_stats = { | |
| "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "duration": f"{elapsed:.2f}s", | |
| "total_hashes": mining_stats["total_hashes"], | |
| "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s", | |
| "blocks_found": mining_stats["blocks_found"], | |
| "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None, | |
| "best_hash_difficulty": mining_stats["best_hash_difficulty"] | |
| } | |
| historical_stats["sessions"].append(session_stats) | |
| historical_stats["total_runtime"] = mining_stats["total_runtime"] + elapsed | |
| historical_stats["total_hashes"] = mining_stats["all_time_total_hashes"] + mining_stats["total_hashes"] | |
| historical_stats["total_blocks"] = sum(s["blocks_found"] for s in historical_stats["sessions"]) | |
| historical_stats["best_session_hashrate"] = max( | |
| mining_stats["best_session_hashrate"], | |
| hash_rate/1000 # Convert to KH/s | |
| ) | |
| with open(STATS_FILE, 'w') as f: | |
| json.dump(historical_stats, f, indent=2) | |
| except Exception as e: | |
| log_mining(f"Error saving mining stats: {str(e)}") | |
| def log_mining(message): | |
| """Add a mining log message with timestamp""" | |
| timestamp = datetime.now().strftime("%H:%M:%S") | |
| log_entry = f"[{timestamp}] {message}" | |
| mining_stats["logs"].append(log_entry) | |
| # Keep only the last 100 logs | |
| if len(mining_stats["logs"]) > 100: | |
| mining_stats["logs"] = mining_stats["logs"][-100:] | |
| print(log_entry) | |
| async def startup_event(): | |
| """Initialize mining components and start mining on startup""" | |
| global miner_instance, mining_thread, mining_stats | |
| if not miner_instance: | |
| log_mining("🚀 Initializing Bitcoin mining components...") | |
| miner_instance = ParallelMiner(num_cores=5) # Increased cores for better performance | |
| # Initialize mining stats | |
| mining_stats["is_mining"] = True | |
| mining_stats["start_time"] = time.time() | |
| mining_stats["total_hashes"] = 0 | |
| mining_stats["blocks_found"] = 0 | |
| mining_stats["best_hash"] = None | |
| # Start mining in background thread | |
| mining_thread = threading.Thread( | |
| target=miner_instance.start_mining, | |
| kwargs={"duration": None} # Run forever | |
| ) | |
| mining_thread.daemon = True | |
| mining_thread.start() | |
| log_mining(f"Started mining automatically with {len(miner_instance.cores)} cores") | |
| from fastapi.staticfiles import StaticFiles | |
| # app.mount("/static", StaticFiles(directory="static"), name="static") | |
| # Serve your main HTML file | |
| async def root(): | |
| return () | |
| # return FileResponse("index.html") | |
| # # Optional: If you need to serve other static files individually | |
| # @app.get("/{filename}") | |
| # async def serve_file(filename: str): | |
| # if filename in ['style.css', 'script.js']: | |
| # return FileResponse(f"static/{filename}") | |
| # return FileResponse(f"static/{filename}") | |
| async def get_mining_status(): | |
| """Get current mining status and statistics""" | |
| if not miner_instance: | |
| return {"error": "Mining system not initialized"} | |
| current_time = time.time() | |
| if mining_stats["start_time"]: | |
| elapsed = current_time - mining_stats["start_time"] | |
| hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0 | |
| else: | |
| hash_rate = 0 | |
| return { | |
| "is_mining": mining_stats["is_mining"], | |
| "total_hashes": mining_stats["total_hashes"], | |
| "hash_rate": f"{hash_rate/1000:.2f} KH/s", | |
| "blocks_found": mining_stats["blocks_found"], | |
| "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None, | |
| "uptime": f"{elapsed:.2f}s" if mining_stats["start_time"] else "0s", | |
| "cores_active": len(miner_instance.cores) if miner_instance else 0, | |
| "units_per_core": len(miner_instance.cores[0].units) if miner_instance and miner_instance.cores else 0, | |
| "logs": mining_stats["logs"][-10:] # Last 10 logs | |
| } | |
| async def get_mining_performance(): | |
| """Get detailed mining performance metrics""" | |
| global miner_instance, mining_stats | |
| if not miner_instance: | |
| return {"error": "Mining system not initialized"} | |
| current_time = time.time() | |
| if mining_stats["start_time"]: | |
| elapsed = current_time - mining_stats["start_time"] | |
| hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0 | |
| hashes_per_core = mining_stats["total_hashes"] / len(miner_instance.cores) if miner_instance.cores else 0 | |
| else: | |
| elapsed = 0 | |
| hash_rate = 0 | |
| hashes_per_core = 0 | |
| core_stats = [] | |
| if miner_instance and miner_instance.cores: | |
| for i, core in enumerate(miner_instance.cores): | |
| core_stats.append({ | |
| "core_id": i, | |
| "active_units": len(core.units), | |
| "status": "active" if mining_stats["is_mining"] else "idle" | |
| }) | |
| return { | |
| "overall_performance": { | |
| "hash_rate": f"{hash_rate/1000:.2f} KH/s", | |
| "total_hashes": mining_stats["total_hashes"], | |
| "blocks_found": mining_stats["blocks_found"], | |
| "uptime": f"{elapsed:.2f}s", | |
| "hashes_per_core": f"{hashes_per_core/1000:.2f}K" | |
| }, | |
| "core_utilization": { | |
| "total_cores": len(miner_instance.cores), | |
| "active_cores": len([c for c in core_stats if c["status"] == "active"]), | |
| "cores": core_stats | |
| }, | |
| "memory_usage": { | |
| "core_memory": len(miner_instance.cores) * 1024 * 1024, # Approximate memory usage per core | |
| "total_allocated": len(miner_instance.cores) * len(miner_instance.cores[0].units) * 1024 if miner_instance.cores else 0 | |
| } | |
| } | |
| from fastapi.encoders import jsonable_encoder | |
| async def start_mining(): | |
| """Start Bitcoin mining operations if not already running""" | |
| global mining_thread, miner_instance, mining_stats | |
| if mining_stats["is_mining"]: | |
| return {"message": "Mining is already running", "status": "already_running"} | |
| if not miner_instance: | |
| miner_instance = ParallelMiner() | |
| # Reset statistics | |
| mining_stats["is_mining"] = True | |
| mining_stats["start_time"] = time.time() | |
| mining_stats["total_hashes"] = 0 | |
| mining_stats["blocks_found"] = 0 | |
| mining_stats["best_hash"] = None | |
| # Start mining in background thread | |
| mining_thread = threading.Thread( | |
| target=miner_instance.start_mining, | |
| kwargs={"duration": None} # Always run indefinitely | |
| ) | |
| mining_thread.daemon = True | |
| mining_thread.start() | |
| log_mining(f"Started mining with {len(miner_instance.cores)} cores") | |
| return { | |
| "message": "Mining started successfully", | |
| "status": "started", | |
| "config": { | |
| "cores": len(miner_instance.cores), | |
| "units_per_core": len(miner_instance.cores[0].units), | |
| "duration": "indefinite", | |
| } | |
| } | |
| async def stop_mining(): | |
| """Stop Bitcoin mining operations""" | |
| global miner_instance, mining_stats | |
| if not mining_stats["is_mining"]: | |
| return {"message": "Mining is not currently running", "status": "not_running"} | |
| if miner_instance: | |
| miner_instance.mining = False | |
| mining_stats["is_mining"] = False | |
| # Calculate final statistics | |
| end_time = time.time() | |
| elapsed = end_time - mining_stats["start_time"] | |
| hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0 | |
| # Update cumulative statistics | |
| mining_stats["total_runtime"] += elapsed | |
| mining_stats["all_time_total_hashes"] += mining_stats["total_hashes"] | |
| mining_stats["best_session_hashrate"] = max( | |
| mining_stats["best_session_hashrate"], | |
| hash_rate/1000 # KH/s | |
| ) | |
| # Save stats to file | |
| save_mining_stats() | |
| # Log comprehensive statistics | |
| log_mining("=== Mining Session Completed ===") | |
| log_mining(f"Session Duration: {elapsed:.2f}s") | |
| log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}") | |
| log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s") | |
| log_mining(f"Blocks Found: {mining_stats['blocks_found']}") | |
| log_mining(f"Best Hash: {mining_stats['best_hash'].hex() if mining_stats['best_hash'] else 'None'}") | |
| log_mining(f"Best Hash Difficulty: {mining_stats['best_hash_difficulty']}") | |
| log_mining("\n=== All-Time Statistics ===") | |
| log_mining(f"Total Runtime: {mining_stats['total_runtime']:.2f}s") | |
| log_mining(f"Total Hashes: {mining_stats['all_time_total_hashes']:,}") | |
| log_mining(f"Total Blocks Found: {mining_stats['blocks_found']}") | |
| log_mining(f"Best Session Hash Rate: {mining_stats['best_session_hashrate']:.2f} KH/s") | |
| return { | |
| "message": "Mining stopped successfully", | |
| "status": "stopped", | |
| "session_stats": { | |
| "duration": f"{elapsed:.2f}s", | |
| "total_hashes": mining_stats["total_hashes"], | |
| "avg_hash_rate": f"{hash_rate/1000:.2f} KH/s", | |
| "blocks_found": mining_stats["blocks_found"], | |
| "best_hash": mining_stats["best_hash"].hex() if mining_stats["best_hash"] else None, | |
| "best_hash_difficulty": mining_stats["best_hash_difficulty"] | |
| }, | |
| "all_time_stats": { | |
| "total_runtime": f"{mining_stats['total_runtime']:.2f}s", | |
| "total_hashes": mining_stats["all_time_total_hashes"], | |
| "total_blocks": mining_stats["blocks_found"], | |
| "best_session_hashrate": f"{mining_stats['best_session_hashrate']:.2f} KH/s" | |
| } | |
| } | |
| return {"message": "Mining instance not found", "status": "error"} | |
| async def get_mining_history(): | |
| """Get historical mining statistics""" | |
| try: | |
| if os.path.exists(STATS_FILE): | |
| with open(STATS_FILE, 'r') as f: | |
| historical_stats = json.load(f) | |
| return historical_stats | |
| else: | |
| return { | |
| "sessions": [], | |
| "total_runtime": 0, | |
| "total_hashes": 0, | |
| "total_blocks": 0, | |
| "best_session_hashrate": 0 | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Error reading mining history: {str(e)}") | |
| def handle_shutdown(): | |
| """Handle graceful shutdown and save statistics""" | |
| if mining_stats["is_mining"] and miner_instance: | |
| log_mining("\n🛑 Server shutdown detected - Saving final mining statistics...") | |
| miner_instance.mining = False | |
| mining_stats["is_mining"] = False | |
| # Calculate and save final statistics | |
| end_time = time.time() | |
| elapsed = end_time - mining_stats["start_time"] | |
| hash_rate = mining_stats["total_hashes"] / elapsed if elapsed > 0 else 0 | |
| # Update cumulative statistics | |
| mining_stats["total_runtime"] += elapsed | |
| mining_stats["all_time_total_hashes"] += mining_stats["total_hashes"] | |
| mining_stats["best_session_hashrate"] = max( | |
| mining_stats["best_session_hashrate"], | |
| hash_rate/1000 | |
| ) | |
| # Log final stats before shutdown | |
| log_mining("=== Final Mining Statistics ===") | |
| log_mining(f"Session Duration: {elapsed:.2f}s") | |
| log_mining(f"Total Hashes: {mining_stats['total_hashes']:,}") | |
| log_mining(f"Average Hash Rate: {hash_rate/1000:.2f} KH/s") | |
| log_mining(f"Blocks Found: {mining_stats['blocks_found']}") | |
| if mining_stats["best_hash"]: | |
| log_mining(f"Best Hash: {mining_stats['best_hash'].hex()}") | |
| # Save stats to file | |
| save_mining_stats() | |
| log_mining("Statistics saved successfully") | |
| log_mining("Server shutting down... Goodbye! 👋") | |
| if __name__ == "__main__": | |
| # Start the FastAPI server | |
| print("Starting Bitcoin Mining API Server...") | |
| print("API Documentation will be available at: http://localhost:8000/docs") | |
| print("API Root endpoint: http://localhost:8000/") | |
| print("\nEndpoints:") | |
| print(" - GET /mining/status - Current mining status") | |
| print(" - GET /mining/performance - Detailed performance metrics") | |
| print(" - GET /mining/history - Historical mining statistics") | |
| print(" - POST /mining/start - Start mining (if stopped)") | |
| print(" - POST /mining/stop - Stop mining and show stats") | |
| print("\nPress Ctrl+C to stop mining and save statistics") | |
| try: | |
| uvicorn.run( | |
| app, | |
| host="0.0.0.0", | |
| port=8000, | |
| log_level="info", | |
| reload=False # Set to False for production | |
| ) | |
| except KeyboardInterrupt: | |
| handle_shutdown() | |
| except Exception as e: | |
| log_mining(f"Error during server operation: {str(e)}") | |
| handle_shutdown() | |
| raise | |