# #!/usr/bin/env python3 # """ # RinCoin Mining Pool Web Interface # Provides web dashboard for pool statistics and miner management # """ # import json # import sqlite3 # import requests # from datetime import datetime, timedelta # from http.server import HTTPServer, BaseHTTPRequestHandler # import threading # import time # from requests.auth import HTTPBasicAuth # class PoolWebInterface: # def __init__(self, pool_db, host='0.0.0.0', port=8080, rpc_host='127.0.0.1', rpc_port=9556, # rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90'): # self.pool_db = pool_db # self.host = host # self.port = port # self.rpc_host = rpc_host # self.rpc_port = rpc_port # self.rpc_user = rpc_user # self.rpc_password = rpc_password # self.chart_time_window = 3600 # 1 hour default, adjustable # def set_chart_time_window(self, seconds): # """Set the chart time window""" # self.chart_time_window = seconds # def format_hashrate(self, hashrate): # """Format hashrate in human readable format""" # if hashrate >= 1e12: # return f"{hashrate/1e12:.2f} TH/s" # elif hashrate >= 1e9: # return f"{hashrate/1e9:.2f} GH/s" # elif hashrate >= 1e6: # return f"{hashrate/1e6:.2f} MH/s" # elif hashrate >= 1e3: # return f"{hashrate/1e3:.2f} KH/s" # elif hashrate >= 0.01: # return f"{hashrate:.2f} H/s" # elif hashrate > 0: # return f"{hashrate*1000:.2f} mH/s" # else: # return "0.00 H/s" # def get_pool_balance(self): # """Get pool wallet balance via RPC""" # try: # url = f"http://{self.rpc_host}:{self.rpc_port}/" # headers = {'content-type': 'text/plain'} # auth = HTTPBasicAuth(self.rpc_user, self.rpc_password) # payload = { # "jsonrpc": "1.0", # "id": "pool_balance", # "method": "getbalance", # "params": [] # } # response = requests.post(url, json=payload, headers=headers, auth=auth, timeout=10) # if response.status_code == 200: # result = response.json() # if 'error' in result and result['error'] is not None: # print(f"RPC Error getting balance: {result['error']}") # return 0.0 # balance = result.get('result', 0) # return float(balance) / 100000000 # Convert from satoshis to RIN # else: # print(f"HTTP Error getting balance: {response.status_code}") # return 0.0 # except Exception as e: # print(f"Error getting pool balance: {e}") # return 0.0 # def get_pool_stats(self): # """Get current pool statistics""" # try: # cursor = self.pool_db.cursor() # # Total miners (ever registered) # cursor.execute('SELECT COUNT(DISTINCT id) FROM miners') # total_miners = cursor.fetchone()[0] # # Active miners (last 5 minutes) # cursor.execute(''' # SELECT COUNT(DISTINCT m.id) FROM miners m # JOIN shares s ON m.id = s.miner_id # WHERE s.submitted > datetime('now', '-5 minutes') # ''') # active_miners = cursor.fetchone()[0] # # Total shares (last 24 hours) # cursor.execute(''' # SELECT COUNT(*) FROM shares # WHERE submitted > datetime('now', '-24 hours') # ''') # total_shares_24h = cursor.fetchone()[0] # # Pool hashrate: sum of miners.last_hashrate (instantaneous) # cursor.execute('SELECT COALESCE(SUM(last_hashrate), 0) FROM miners') # hashrate = cursor.fetchone()[0] or 0.0 # # Debug stats # cursor.execute(''' # SELECT SUM(difficulty), COUNT(*) FROM shares # WHERE submitted > datetime('now', '-5 minutes') # ''') # rd = cursor.fetchone() # recent_difficulty = rd[0] if rd and rd[0] else 0 # recent_share_count = rd[1] if rd and rd[1] else 0 # # Get historical hashrate data for chart # cursor.execute(''' # SELECT # strftime('%H:%M', submitted) as time, # COUNT(*) as shares, # SUM(difficulty) as total_difficulty # FROM shares # WHERE submitted > datetime('now', '-{} seconds') # GROUP BY strftime('%Y-%m-%d %H:%M', submitted) # ORDER BY submitted DESC # LIMIT 60 # '''.format(self.chart_time_window)) # historical_data = cursor.fetchall() # # Calculate individual miner hashrates # cursor.execute(''' # SELECT # m.user, m.worker, # COUNT(s.id) as shares, # SUM(s.difficulty) as total_difficulty, # m.last_share # FROM miners m # LEFT JOIN shares s ON m.id = s.miner_id # AND s.submitted > datetime('now', '-5 minutes') # GROUP BY m.id, m.user, m.worker # ORDER BY shares DESC # ''') # miner_stats = cursor.fetchall() # # Calculate individual hashrates (use miners.last_hashrate) # miner_hashrates = [] # for user, worker, shares, difficulty, last_share in miner_stats: # cursor.execute('SELECT last_hashrate FROM miners WHERE user = ? AND worker = ? LIMIT 1', (user, worker)) # row = cursor.fetchone() # miner_hashrate = row[0] if row and row[0] else 0.0 # miner_hashrates.append((user, worker, shares, miner_hashrate, last_share)) # # Total blocks found # cursor.execute('SELECT COUNT(*) FROM blocks') # total_blocks = cursor.fetchone()[0] # # Recent blocks # cursor.execute(''' # SELECT block_hash, height, reward, found_at # FROM blocks # ORDER BY found_at DESC # LIMIT 10 # ''') # recent_blocks = cursor.fetchall() # # Top miners (last 24 hours) - show all miners, even without shares # cursor.execute(''' # SELECT m.user, m.worker, # COALESCE(COUNT(s.id), 0) as shares, # m.last_share, # m.created # FROM miners m # LEFT JOIN shares s ON m.id = s.miner_id # AND s.submitted > datetime('now', '-24 hours') # GROUP BY m.id, m.user, m.worker # ORDER BY shares DESC, m.created DESC # LIMIT 20 # ''') # top_miners = cursor.fetchall() # # All active miners (for better visibility) # cursor.execute(''' # SELECT user, worker, created, last_share # FROM miners # ORDER BY created DESC # LIMIT 10 # ''') # all_miners = cursor.fetchall() # # Get pool balance # pool_balance = self.get_pool_balance() # return { # 'total_miners': total_miners, # 'active_miners': active_miners, # 'total_shares_24h': total_shares_24h, # 'hashrate': hashrate, # 'total_blocks': total_blocks, # 'recent_blocks': recent_blocks, # 'top_miners': top_miners, # 'all_miners': all_miners, # 'miner_hashrates': miner_hashrates, # 'historical_data': historical_data, # 'pool_balance': pool_balance, # 'debug': { # 'recent_difficulty': recent_difficulty, # 'recent_share_count': recent_share_count, # 'total_shares_24h': total_shares_24h # } # } # except Exception as e: # print(f"Error getting pool stats: {e}") # return {} # def generate_html(self, stats): # """Generate HTML dashboard""" # html = f""" # # #
#Distribute block rewards among multiple miners
#24h Shares: {stats.get('total_shares_24h', 0):,}
#Pool Fee: 1%
#Pool Balance: {stats.get('pool_balance', 0):.8f} RIN
#Connection String: stratum+tcp://YOUR_IP:3333
Recent Difficulty (5min): {stats.get('debug', {}).get('recent_difficulty', 0):.6f}
#Recent Share Count (5min): {stats.get('debug', {}).get('recent_share_count', 0)}
#Total Shares (24h): {stats.get('debug', {}).get('total_shares_24h', 0):,}
#Active Miners: {stats.get('active_miners', 0)} (last 5 minutes)
#Total Miners: {stats.get('total_miners', 0)} (ever registered)
#| User | #Worker | #Connected | #Last Share | #
|---|---|---|---|
| {user} | #{worker} | #{created} | #{last_share or 'Never'} | #
| No miners connected | #|||
| User | #Worker | #Shares | #Hashrate | #Last Share | #
|---|---|---|---|---|
| {user} | #{worker} | #{shares:,} | #{self.format_hashrate(hashrate)} | #{last_share or 'Never'} | #
| No shares submitted yet | #||||
| Height | #Hash | #Reward | #Found At | #
|---|---|---|---|
| {height} | #{block_hash[:16]}... | #{reward:.8f} RIN | #{found_at} | #
Use any RinHash-compatible miner:
#./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x
# Replace YOUR_IP with your server's public IP address
#