# #!/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""" # # # # RinCoin Mining Pool # # # # # #
#
#

šŸŠā€ā™‚ļø RinCoin Mining Pool

#

Distribute block rewards among multiple miners

#
# #
#
#
{stats.get('total_miners', 0)}
#
Total Miners
#
#
#
{stats.get('active_miners', 0)}
#
Active Miners
#
#
#
{self.format_hashrate(stats.get('hashrate', 0))}
#
Hashrate
#
#
#
{stats.get('total_blocks', 0)}
#
Blocks Found
#
#
#
{stats.get('pool_balance', 0):.2f}
#
Pool Balance (RIN)
#
#
#
#

šŸ“Š Pool Statistics

#

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

# #
# šŸ” Debug Info #

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)

#
#
#
#

šŸ“ˆ Hashrate Chart

#
# # #
#
# #
#
#
#

šŸ‘„ Connected Miners

# # # # # # # # """ # for miner in stats.get('all_miners', []): # user, worker, created, last_share = miner # html += f""" # # # # # # # """ # if not stats.get('all_miners', []): # html += """ # # # # """ # html += """ #
UserWorkerConnectedLast Share
{user}{worker}{created}{last_share or 'Never'}
No miners connected
#
#
#

šŸ† Top Miners (24h Shares)

# # # # # # # # # """ # for miner in stats.get('miner_hashrates', []): # user, worker, shares, hashrate, last_share = miner # html += f""" # # # # # # # # """ # if not stats.get('top_miners', []): # html += """ # # # # """ # html += """ #
UserWorkerSharesHashrateLast Share
{user}{worker}{shares:,}{self.format_hashrate(hashrate)}{last_share or 'Never'}
No shares submitted yet
#
#
#

šŸ† Recent Blocks

# # # # # # # # """ # for block in stats.get('recent_blocks', []): # block_hash, height, reward, found_at = block # html += f""" # # # # # # # """ # html += """ #
HeightHashRewardFound At
{height}{block_hash[:16]}...{reward:.8f} RIN{found_at}
#
#
#

šŸ”— Connect to Pool

#

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

#
#
# # # # # # """ # return html # class PoolWebHandler(BaseHTTPRequestHandler): # def __init__(self, *args, pool_interface=None, **kwargs): # self.pool_interface = pool_interface # super().__init__(*args, **kwargs) # def do_GET(self): # if self.path == '/': # stats = self.pool_interface.get_pool_stats() # html = self.pool_interface.generate_html(stats) # self.send_response(200) # self.send_header('Content-type', 'text/html') # self.end_headers() # self.wfile.write(html.encode('utf-8')) # elif self.path == '/api/stats': # stats = self.pool_interface.get_pool_stats() # self.send_response(200) # self.send_header('Content-type', 'application/json') # self.end_headers() # self.wfile.write(json.dumps(stats).encode('utf-8')) # else: # self.send_response(404) # self.end_headers() # self.wfile.write(b'Not Found') # def log_message(self, format, *args): # # Suppress access logs # pass # def start_web_interface(pool_db, host='0.0.0.0', port=8083, rpc_host='127.0.0.1', rpc_port=9556, # rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90'): # """Start the web interface server""" # interface = PoolWebInterface(pool_db, host, port, rpc_host, rpc_port, rpc_user, rpc_password) # class Handler(PoolWebHandler): # def __init__(self, *args, **kwargs): # super().__init__(*args, pool_interface=interface, **kwargs) # try: # server = HTTPServer((host, port), Handler) # print(f"🌐 Web interface running on http://{host}:{port}") # print("Press Ctrl+C to stop") # server.serve_forever() # except OSError as e: # if "Address already in use" in str(e): # print(f"āš ļø Port {port} is already in use, web interface not started") # print(f"šŸ’” Try a different port or kill the process using port {port}") # else: # print(f"āŒ Failed to start web interface: {e}") # except KeyboardInterrupt: # print("\nšŸ›‘ Shutting down web interface...") # server.shutdown() # if __name__ == "__main__": # # This would be called from the main pool server # print("Web interface module loaded")