mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 07:57:19 -05:00
320 lines
9.5 KiB
Python
320 lines
9.5 KiB
Python
"""Mining statistics and control router - RoadCoin Miner integration."""
|
|
import asyncio
|
|
import hashlib
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, desc
|
|
from pydantic import BaseModel
|
|
|
|
from app.database import get_db
|
|
from app.models.blockchain import Block, Wallet
|
|
from app.models.user import User
|
|
from app.routers.auth import get_current_user
|
|
from app.utils import utc_now
|
|
|
|
router = APIRouter(prefix="/api/miner", tags=["miner"])
|
|
|
|
|
|
# In-memory miner state (for simulation)
|
|
class MinerState:
|
|
"""Global miner state."""
|
|
|
|
def __init__(self):
|
|
self.is_mining = False
|
|
self.hashrate_mhs = 0.0
|
|
self.shares_submitted = 0
|
|
self.shares_accepted = 0
|
|
self.pool_url = "pool.roadcoin.network:3333"
|
|
self.worker_id = "RoadMiner-1"
|
|
self.started_at: Optional[datetime] = None
|
|
self.temperature_celsius = 65.0
|
|
self.power_watts = 120.0
|
|
|
|
|
|
miner_state = MinerState()
|
|
|
|
|
|
# Schemas
|
|
class MinerStatus(BaseModel):
|
|
"""Current miner status."""
|
|
|
|
is_mining: bool
|
|
hashrate_mhs: float
|
|
shares_submitted: int
|
|
shares_accepted: int
|
|
shares_rejected: int
|
|
pool_url: str
|
|
worker_id: str
|
|
uptime_seconds: int
|
|
temperature_celsius: float
|
|
power_watts: float
|
|
efficiency_mhs_per_watt: float
|
|
|
|
|
|
class MinerStats(BaseModel):
|
|
"""Miner statistics."""
|
|
|
|
blocks_mined: int
|
|
roadcoins_earned: float
|
|
current_hashrate_mhs: float
|
|
average_hashrate_mhs: float
|
|
total_shares: int
|
|
accepted_shares: int
|
|
rejected_shares: int
|
|
last_block_time: Optional[datetime]
|
|
mining_since: Optional[datetime]
|
|
|
|
|
|
class MinerControl(BaseModel):
|
|
"""Miner control commands."""
|
|
|
|
action: str # start, stop, restart
|
|
pool_url: Optional[str] = None
|
|
worker_id: Optional[str] = None
|
|
|
|
|
|
class RecentBlock(BaseModel):
|
|
"""Recent mined block info."""
|
|
|
|
block_index: int
|
|
block_hash: str
|
|
reward: float
|
|
timestamp: datetime
|
|
difficulty: int
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Routes
|
|
|
|
@router.get("/status", response_model=MinerStatus)
|
|
async def get_miner_status(
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get current miner status and performance metrics."""
|
|
uptime_seconds = 0
|
|
if miner_state.started_at:
|
|
uptime_seconds = int((utc_now() - miner_state.started_at).total_seconds())
|
|
|
|
# Simulate some variance in hashrate
|
|
current_hashrate = miner_state.hashrate_mhs
|
|
if miner_state.is_mining:
|
|
current_hashrate = miner_state.hashrate_mhs + random.uniform(-2.0, 2.0)
|
|
|
|
# Calculate efficiency
|
|
efficiency = 0.0
|
|
if miner_state.power_watts > 0:
|
|
efficiency = current_hashrate / miner_state.power_watts
|
|
|
|
rejected_shares = miner_state.shares_submitted - miner_state.shares_accepted
|
|
|
|
return MinerStatus(
|
|
is_mining=miner_state.is_mining,
|
|
hashrate_mhs=round(current_hashrate, 2),
|
|
shares_submitted=miner_state.shares_submitted,
|
|
shares_accepted=miner_state.shares_accepted,
|
|
shares_rejected=rejected_shares,
|
|
pool_url=miner_state.pool_url,
|
|
worker_id=miner_state.worker_id,
|
|
uptime_seconds=uptime_seconds,
|
|
temperature_celsius=miner_state.temperature_celsius,
|
|
power_watts=miner_state.power_watts,
|
|
efficiency_mhs_per_watt=round(efficiency, 4),
|
|
)
|
|
|
|
|
|
@router.get("/stats", response_model=MinerStats)
|
|
async def get_miner_stats(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get overall mining statistics."""
|
|
# Get user's wallet
|
|
wallet_result = await db.execute(
|
|
select(Wallet).filter(Wallet.user_id == current_user.id)
|
|
)
|
|
wallet = wallet_result.scalar_one_or_none()
|
|
|
|
if not wallet:
|
|
return MinerStats(
|
|
blocks_mined=0,
|
|
roadcoins_earned=0.0,
|
|
current_hashrate_mhs=miner_state.hashrate_mhs if miner_state.is_mining else 0.0,
|
|
average_hashrate_mhs=0.0,
|
|
total_shares=miner_state.shares_submitted,
|
|
accepted_shares=miner_state.shares_accepted,
|
|
rejected_shares=miner_state.shares_submitted - miner_state.shares_accepted,
|
|
last_block_time=None,
|
|
mining_since=miner_state.started_at,
|
|
)
|
|
|
|
miner_filter = Block.miner_address == wallet.address
|
|
|
|
# Count blocks mined by this user
|
|
blocks_count_result = await db.execute(
|
|
select(func.count(Block.id)).filter(miner_filter)
|
|
)
|
|
blocks_mined = blocks_count_result.scalar() or 0
|
|
|
|
# Sum rewards earned
|
|
rewards_result = await db.execute(
|
|
select(func.sum(Block.reward)).filter(miner_filter)
|
|
)
|
|
roadcoins_earned = rewards_result.scalar() or 0.0
|
|
|
|
# Get last block mined
|
|
last_block_result = await db.execute(
|
|
select(Block)
|
|
.filter(miner_filter)
|
|
.order_by(desc(Block.timestamp))
|
|
.limit(1)
|
|
)
|
|
last_block = last_block_result.scalar_one_or_none()
|
|
|
|
# Calculate average hashrate (simulated based on blocks mined)
|
|
average_hashrate = 0.0
|
|
if blocks_mined > 0:
|
|
# Rough estimate: difficulty 4 = ~40 MH/s average
|
|
average_hashrate = 40.0 + (blocks_mined * 0.5)
|
|
|
|
return MinerStats(
|
|
blocks_mined=blocks_mined,
|
|
roadcoins_earned=float(roadcoins_earned),
|
|
current_hashrate_mhs=miner_state.hashrate_mhs if miner_state.is_mining else 0.0,
|
|
average_hashrate_mhs=round(average_hashrate, 2),
|
|
total_shares=miner_state.shares_submitted,
|
|
accepted_shares=miner_state.shares_accepted,
|
|
rejected_shares=miner_state.shares_submitted - miner_state.shares_accepted,
|
|
last_block_time=last_block.timestamp if last_block else None,
|
|
mining_since=miner_state.started_at,
|
|
)
|
|
|
|
|
|
@router.get("/blocks", response_model=List[RecentBlock])
|
|
async def get_recent_blocks(
|
|
limit: int = 10,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get recently mined blocks by this user."""
|
|
# Get user's wallet
|
|
wallet_result = await db.execute(
|
|
select(Wallet).filter(Wallet.user_id == current_user.id)
|
|
)
|
|
wallet = wallet_result.scalar_one_or_none()
|
|
|
|
if not wallet:
|
|
return []
|
|
|
|
# Get recent blocks
|
|
blocks_result = await db.execute(
|
|
select(Block)
|
|
.filter(Block.miner_address == wallet.address)
|
|
.order_by(desc(Block.timestamp))
|
|
.limit(limit)
|
|
)
|
|
blocks = blocks_result.scalars().all()
|
|
|
|
return [
|
|
RecentBlock(
|
|
block_index=block.index,
|
|
block_hash=block.hash,
|
|
reward=block.reward,
|
|
timestamp=block.timestamp,
|
|
difficulty=block.difficulty,
|
|
)
|
|
for block in blocks
|
|
]
|
|
|
|
|
|
@router.post("/control")
|
|
async def control_miner(
|
|
control: MinerControl,
|
|
background_tasks: BackgroundTasks,
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Control miner operations (start/stop/restart)."""
|
|
if control.action == "start":
|
|
if miner_state.is_mining:
|
|
raise HTTPException(status_code=400, detail="Miner is already running")
|
|
|
|
miner_state.is_mining = True
|
|
miner_state.started_at = utc_now()
|
|
miner_state.hashrate_mhs = random.uniform(38.0, 45.0) # Simulate hashrate
|
|
|
|
if control.pool_url:
|
|
miner_state.pool_url = control.pool_url
|
|
if control.worker_id:
|
|
miner_state.worker_id = control.worker_id
|
|
|
|
# Start background mining simulation
|
|
background_tasks.add_task(simulate_mining)
|
|
|
|
return {"message": "Miner started successfully", "status": "running"}
|
|
|
|
elif control.action == "stop":
|
|
if not miner_state.is_mining:
|
|
raise HTTPException(status_code=400, detail="Miner is not running")
|
|
|
|
miner_state.is_mining = False
|
|
miner_state.hashrate_mhs = 0.0
|
|
|
|
return {"message": "Miner stopped successfully", "status": "stopped"}
|
|
|
|
elif control.action == "restart":
|
|
miner_state.is_mining = False
|
|
await asyncio.sleep(1)
|
|
miner_state.is_mining = True
|
|
miner_state.started_at = utc_now()
|
|
miner_state.hashrate_mhs = random.uniform(38.0, 45.0)
|
|
|
|
background_tasks.add_task(simulate_mining)
|
|
|
|
return {"message": "Miner restarted successfully", "status": "running"}
|
|
|
|
else:
|
|
raise HTTPException(status_code=400, detail=f"Invalid action: {control.action}")
|
|
|
|
|
|
async def simulate_mining():
|
|
"""Background task to simulate mining activity."""
|
|
while miner_state.is_mining:
|
|
# Simulate share submission every 10-30 seconds
|
|
await asyncio.sleep(random.uniform(10, 30))
|
|
|
|
if not miner_state.is_mining:
|
|
break
|
|
|
|
miner_state.shares_submitted += 1
|
|
|
|
# 95% acceptance rate
|
|
if random.random() < 0.95:
|
|
miner_state.shares_accepted += 1
|
|
|
|
# Vary hashrate slightly
|
|
miner_state.hashrate_mhs = random.uniform(38.0, 45.0)
|
|
|
|
# Vary temperature
|
|
miner_state.temperature_celsius = random.uniform(60.0, 75.0)
|
|
|
|
|
|
@router.get("/pool/info")
|
|
async def get_pool_info(
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get mining pool information."""
|
|
return {
|
|
"pool_url": miner_state.pool_url,
|
|
"pool_name": "RoadCoin Mining Pool",
|
|
"pool_hashrate": "2.4 GH/s",
|
|
"connected_miners": 142,
|
|
"pool_fee": "1%",
|
|
"min_payout": 10.0,
|
|
"payment_interval_hours": 24,
|
|
"last_block_found": (utc_now() - timedelta(minutes=random.randint(5, 120))).isoformat(),
|
|
}
|