stratum working, implement mining pool, support "extranonce"

This commit is contained in:
Dobromir Popov
2025-09-02 11:54:15 +03:00
parent e816c04c8b
commit 8bdf9bf9eb
10 changed files with 1339 additions and 25 deletions

View File

@@ -0,0 +1,46 @@
# RinCoin Mining Quick Reference
## 🚀 **Quick Commands:**
### **Solo Mining (All Rewards to You)**
```bash
# Start solo mining proxy
./MINE/rin/start_stratum_proxy.sh
# Connect miner
./cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3333 -u user -p pass -t 28
```
### **Mining Pool (Distribute Rewards)**
```bash
# Start mining pool
./MINE/rin/start_mining_pool.sh
# Miners connect
./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x
```
### **Cleanup**
```bash
# Kill proxy/pool processes
./MINE/rin/kill_stratum_proxy.sh
```
## 📊 **What Each Does:**
| Command | Purpose | Rewards | Miners |
|---------|---------|---------|--------|
| `start_stratum_proxy.sh` | Solo mining | 100% to you | Single |
| `start_mining_pool.sh` | Pool mining | Distributed | Multiple |
## 🌐 **Web Dashboard (Pool Only)**
- **URL**: `http://YOUR_IP:8080`
- **Features**: Stats, miners, blocks, hashrate
## ⚡ **Quick Test**
```bash
# Test solo mining
./MINE/rin/start_stratum_proxy.sh &
sleep 5
./cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3333 -u user -p pass -t 4
```

View File

@@ -1,4 +1,39 @@
# RinCoin Solo Mining Setup Complete! 🎉
# RinCoin Mining Setup Complete! 🎉
## 🎯 **Choose Your Mining Strategy:**
### **Option 1: Solo Mining (Single Miner, All Rewards to You)**
```bash
# Start solo mining proxy
./MINE/rin/start_stratum_proxy.sh
# Run your miner
./cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3333 -u user -p pass -t 28
```
**Result**: 100% of block rewards go to your wallet
### **Option 2: Mining Pool (Multiple Miners, Distributed Rewards)**
```bash
# Start mining pool
./MINE/rin/start_mining_pool.sh
# Miners connect with:
./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x
```
**Result**: Block rewards distributed among all miners based on shares
### **Key Differences:**
| Feature | Solo Mining (`stratum_proxy`) | Mining Pool (`stratum_pool`) |
|---------|--------------------------------|------------------------------|
| **Rewards** | 100% to you | Distributed among miners |
| **Miners** | Single | Multiple |
| **Setup** | Simple | More complex |
| **Consistency** | Rare big payouts | Regular small payments |
| **Risk** | High variance | Lower variance |
| **Public** | No | Yes, can be published |
---
## ✅ **Successfully Built and Running:**
@@ -21,23 +56,35 @@
- **Data Directory**: `/mnt/data/docker_vol/rincoin/rincoin-node/data`
- **Docker Compose**: `MINE/rin/container.yml`
## 🚀 **Ready for Solo Mining:**
## 🚀 **Ready for Mining:**
### **Pool Mining (Zergpool)**
### **Pool Mining (Zergpool) - Recommended for Consistent Rewards**
```bash
# CPU Mining RinHash to BTC
sudo docker exec -it amd-strix-halo-llama-rocm bash -c "/mnt/dl/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://rinhash.mine.zergpool.com:7148 -u bc1qjn4m6rmrveuxhk02a5qhe4r6kdcsvvt3vhdn9j -p c=BTC,mc=RIN,ID=StrixHalo -t 32"
```
### **Solo Mining (Local Node)**
### **Solo Mining (Local Node) - With Stratum Proxy ⭐ RECOMMENDED**
```bash
# Solo mining with default address and all cores
bash MINE/rin/solo_mining_core.sh
# Start mining with your RinCoin address (rewards go to this address!)
./MINE/rin/start_mining_with_address.sh rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q 28
# Solo mining with custom address and thread count
bash MINE/rin/solo_mining_core.sh -a rin1qkeweeh2agurz5af3eullkvtl34kcmrwl2ytkvn -t 16
# Or use the default address
./MINE/rin/start_mining_with_address.sh
```
# Solo mining with 28 threads (like your cpuminer attempt)
### **Manual Solo Mining Setup (Stratum Proxy)**
```bash
# 1. Start Stratum proxy (solo mining)
./MINE/rin/start_stratum_proxy.sh
# 2. In another terminal, connect cpuminer-opt-rin
sudo docker exec -it amd-strix-halo-llama-rocm bash -c "/mnt/dl/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://172.17.0.1:3333 -u user -p pass -t 28"
```
### **Built-in Core Mining (Low Performance)**
```bash
# Solo mining with built-in RinCoin core (not recommended)
bash MINE/rin/solo_mining_core.sh -t 28
```
@@ -52,19 +99,96 @@ sudo docker exec -it amd-strix-halo-llama-rocm bash -c "/mnt/dl/rinhash/cpuminer
# - No built-in protocol conversion available
```
### ** we do not need a container for the cpu, so we can run it directly if we want**
1. we run stratum proxy
### **Direct CPU Mining Setup (Solo Mining - No Container Needed)**
```bash
# 1. Start stratum proxy (solo mining)
./MINE/rin/start_stratum_proxy.sh
2. we run the cpuminer
# OR run in background with logging
nohup python3 MINE/rin/stratum_proxy.py > stratum_proxy.log 2>&1 &
# 2. Run cpuminer directly on host
/home/db/Downloads/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3333 -u user -p pass -t 28
# 3. Clean up when done
./MINE/rin/kill_stratum_proxy.sh
```
### **Mining Options Explained**
1. **Built-in Core Mining**: Uses RinCoin's `generatetoaddress` RPC command
2. **Pool Mining**: Uses cpuminer-opt-rin with Stratum pools (Zergpool)
1. **Built-in Core Mining**: Uses RinCoin's `generatetoaddress` RPC command (low performance)
2. **Pool Mining**: Uses cpuminer-opt-rin with Stratum pools (Zergpool) - consistent rewards
3. **Direct RPC Mining**: Would require custom miner implementing `getblocktemplate`
4. **Solo Mining (Stratum Proxy)**: Uses Stratum proxy to bridge cpuminer-opt-rin to RinCoin node - all rewards to you
5. **Mining Pool (Stratum Pool)**: Distributes block rewards among multiple miners - share-based rewards
## 🏊‍♂️ **Mining Pool Setup (Multiple Miners)**
Your Stratum proxy can be enhanced to work as a **full mining pool** that distributes block rewards among multiple miners!
### **Pool Features:**
-**Multiple Miner Support**: Unlimited miners can connect
-**Share-Based Rewards**: Rewards distributed based on share contributions
-**Pool Fee**: 1% fee for pool maintenance
-**Real-Time Statistics**: Web dashboard with live stats
-**Block Reward Distribution**: Automatic distribution when blocks are found
### **Quick Start Pool:**
```bash
# 1. Start the mining pool
./MINE/rin/start_mining_pool.sh
# 2. Miners connect with:
./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x
```
### **Pool vs Solo Mining:**
| Feature | Solo Mining | Mining Pool |
|---------|-------------|-------------|
| **Block Rewards** | 100% to you | Distributed among miners |
| **Consistency** | Rare blocks | Regular small payments |
| **Setup** | Simple | More complex |
| **Miners** | Single | Multiple |
| **Risk** | High variance | Lower variance |
### **Publishing Your Pool:**
#### **1. Public IP Setup:**
```bash
# Get your public IP
curl ifconfig.me
# Configure firewall (if needed)
sudo ufw allow 3333/tcp
sudo ufw allow 8080/tcp # Web interface
```
#### **2. Pool Connection String:**
```
stratum+tcp://YOUR_PUBLIC_IP:3333
```
#### **3. Web Dashboard:**
- **URL**: `http://YOUR_PUBLIC_IP:8080`
- **Features**: Real-time stats, miner rankings, block history
#### **4. Pool Announcement:**
Share your pool details:
- **Algorithm**: RinHash
- **Port**: 3333
- **Fee**: 1%
- **Payout**: Automatic distribution
- **Web**: `http://YOUR_PUBLIC_IP:8080`
### **Pool Configuration:**
```python
# Edit MINE/rin/stratum_pool.py
pool_address = 'rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q' # Pool wallet
pool_fee_percent = 1.0 # Pool fee percentage
```
## build image
sudo bash -lc "cd /mnt/shared/DEV/repos/d-popov.com/scripts/MINE/rin && docker build -t rincoin-node:latest . | cat"
@@ -150,6 +274,36 @@ curl --user rinrpc:745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90 \
2. **Solo Mining**: Very low chance of finding blocks solo. Consider pool mining for consistent rewards.
3. **RPC Access**: ✅ **WORKING** - RPC is accessible on port 9556
4. **Address Parameter**: Solo mining script accepts custom addresses or uses default
5. **Block Rewards**: When solo mining, ALL block rewards go to your specified RinCoin address
## 🛠️ **Troubleshooting:**
### **Port 3333 Already in Use**
```bash
# Check what's using the port
sudo netstat -tlnp | grep :3333
# Kill existing processes
./MINE/rin/kill_stratum_proxy.sh
# Or manually kill
sudo lsof -ti:3333 | xargs sudo kill -9
```
### **Container Can't Connect to Proxy**
```bash
# Use Docker gateway IP instead of localhost
sudo docker exec -it amd-strix-halo-llama-rocm bash -c "/mnt/dl/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://172.17.0.1:3333 -u user -p pass -t 28"
```
### **Check Proxy Logs**
```bash
# View real-time logs
tail -f stratum_proxy.log
# Check if proxy is running
ps aux | grep stratum_proxy
```
## 🎯 **Next Steps:**

View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Kill RinCoin Stratum Proxy processes
echo "=== Killing RinCoin Stratum Proxy ==="
echo ""
# Find and kill Python processes running stratum_proxy.py
PIDS=$(ps aux | grep "stratum_proxy.py" | grep -v grep | awk '{print $2}')
if [ -n "$PIDS" ]; then
echo "Found Stratum Proxy processes: $PIDS"
echo "Killing processes..."
for pid in $PIDS; do
kill -9 "$pid" 2>/dev/null && echo "Killed PID: $pid" || echo "Failed to kill PID: $pid"
done
else
echo "No Stratum Proxy processes found"
fi
# Also kill any process using port 3333
echo ""
echo "Checking port 3333..."
PORT_PIDS=$(sudo lsof -ti:3333 2>/dev/null)
if [ -n "$PORT_PIDS" ]; then
echo "Found processes using port 3333: $PORT_PIDS"
echo "Killing processes..."
for pid in $PORT_PIDS; do
sudo kill -9 "$pid" 2>/dev/null && echo "Killed PID: $pid" || echo "Failed to kill PID: $pid"
done
else
echo "No processes using port 3333"
fi
echo ""
echo "✅ Cleanup complete!"
echo ""
echo "Port 3333 status:"
netstat -tln | grep ":3333 " || echo "Port 3333 is free"

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env python3
"""
RinCoin Mining Pool Web Interface
Provides web dashboard for pool statistics and miner management
"""
import json
import sqlite3
from datetime import datetime, timedelta
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading
import time
class PoolWebInterface:
def __init__(self, pool_db, host='0.0.0.0', port=8080):
self.pool_db = pool_db
self.host = host
self.port = port
def get_pool_stats(self):
"""Get current pool statistics"""
try:
cursor = self.pool_db.cursor()
# Total miners
cursor.execute('SELECT COUNT(DISTINCT id) FROM miners')
total_miners = cursor.fetchone()[0]
# Active miners (last 10 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', '-10 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 estimate
cursor.execute('''
SELECT COUNT(*) FROM shares
WHERE submitted > datetime('now', '-5 minutes')
''')
recent_shares = cursor.fetchone()[0]
hashrate = recent_shares * 12 # Rough estimate
# 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
cursor.execute('''
SELECT m.user, m.worker, COUNT(s.id) as shares, m.last_share
FROM miners m
JOIN shares s ON m.id = s.miner_id
WHERE s.submitted > datetime('now', '-24 hours')
GROUP BY m.id
ORDER BY shares DESC
LIMIT 10
''')
top_miners = cursor.fetchall()
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
}
except Exception as e:
print(f"Error getting pool stats: {e}")
return {}
def generate_html(self, stats):
"""Generate HTML dashboard"""
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>RinCoin Mining Pool</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{ font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }}
.container {{ max-width: 1200px; margin: 0 auto; }}
.header {{ background: #2c3e50; color: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }}
.stats-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; }}
.stat-card {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
.stat-value {{ font-size: 2em; font-weight: bold; color: #3498db; }}
.stat-label {{ color: #7f8c8d; margin-top: 5px; }}
.section {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }}
.section h2 {{ margin-top: 0; color: #2c3e50; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }}
th {{ background: #f8f9fa; font-weight: bold; }}
.block-hash {{ font-family: monospace; font-size: 0.9em; }}
.refresh-btn {{ background: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; }}
.refresh-btn:hover {{ background: #2980b9; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🏊‍♂️ RinCoin Mining Pool</h1>
<p>Distribute block rewards among multiple miners</p>
</div>
<button class="refresh-btn" onclick="location.reload()">🔄 Refresh</button>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">{stats.get('total_miners', 0)}</div>
<div class="stat-label">Total Miners</div>
</div>
<div class="stat-card">
<div class="stat-value">{stats.get('active_miners', 0)}</div>
<div class="stat-label">Active Miners</div>
</div>
<div class="stat-card">
<div class="stat-value">{stats.get('hashrate', 0):.2f}</div>
<div class="stat-label">Hashrate (H/s)</div>
</div>
<div class="stat-card">
<div class="stat-value">{stats.get('total_blocks', 0)}</div>
<div class="stat-label">Blocks Found</div>
</div>
</div>
<div class="section">
<h2>📊 Pool Statistics</h2>
<p><strong>24h Shares:</strong> {stats.get('total_shares_24h', 0):,}</p>
<p><strong>Pool Fee:</strong> 1%</p>
<p><strong>Connection String:</strong> <code>stratum+tcp://YOUR_IP:3333</code></p>
</div>
<div class="section">
<h2>👥 Top Miners (24h)</h2>
<table>
<tr>
<th>User</th>
<th>Worker</th>
<th>Shares</th>
<th>Last Share</th>
</tr>
"""
for miner in stats.get('top_miners', []):
user, worker, shares, last_share = miner
html += f"""
<tr>
<td>{user}</td>
<td>{worker}</td>
<td>{shares:,}</td>
<td>{last_share or 'Never'}</td>
</tr>
"""
html += """
</table>
</div>
<div class="section">
<h2>🏆 Recent Blocks</h2>
<table>
<tr>
<th>Height</th>
<th>Hash</th>
<th>Reward</th>
<th>Found At</th>
</tr>
"""
for block in stats.get('recent_blocks', []):
block_hash, height, reward, found_at = block
html += f"""
<tr>
<td>{height}</td>
<td class="block-hash">{block_hash[:16]}...</td>
<td>{reward:.8f} RIN</td>
<td>{found_at}</td>
</tr>
"""
html += """
</table>
</div>
<div class="section">
<h2>🔗 Connect to Pool</h2>
<p>Use any RinHash-compatible miner:</p>
<pre><code>./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x</code></pre>
<p><strong>Replace YOUR_IP with your server's public IP address</strong></p>
</div>
</div>
<script>
// Auto-refresh every 30 seconds
setTimeout(() => location.reload(), 30000);
</script>
</body>
</html>
"""
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=8080):
"""Start the web interface server"""
interface = PoolWebInterface(pool_db, host, port)
class Handler(PoolWebHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, pool_interface=interface, **kwargs)
server = HTTPServer((host, port), Handler)
print(f"🌐 Web interface running on http://{host}:{port}")
print("Press Ctrl+C to stop")
try:
server.serve_forever()
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")

View File

@@ -0,0 +1,71 @@
#!/bin/bash
# RinCoin Mining Pool Server Startup Script
# Distributes block rewards among multiple miners
echo "=== RinCoin Mining Pool Server ==="
echo ""
# Check if RinCoin node is running
echo "Checking RinCoin node status..."
if ! curl -s -u rinrpc:745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90 \
-H 'content-type: text/plain' \
--data '{"jsonrpc":"1.0","id":"curl","method":"getblockchaininfo","params":[]}' \
http://127.0.0.1:9556/ > /dev/null; then
echo "❌ RinCoin node is not running!"
echo "Start it first with: docker start rincoin-node"
exit 1
fi
echo "✅ RinCoin node is running"
# Check Python dependencies
echo "Checking Python dependencies..."
python3 -c "import requests, sqlite3" 2>/dev/null || {
echo "Installing python3-requests..."
sudo apt-get update && sudo apt-get install -y python3-requests
}
echo "✅ Python dependencies ready"
# Check if port 3333 is already in use
if netstat -tln | grep -q ":3333 "; then
echo ""
echo "⚠️ Port 3333 is already in use!"
echo ""
echo "🔍 Process using port 3333:"
sudo netstat -tlnp | grep ":3333 " || echo "Could not determine process"
echo ""
echo "🛑 To kill existing process:"
echo "sudo lsof -ti:3333 | xargs sudo kill -9"
echo ""
read -p "Kill existing process and continue? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Killing processes using port 3333..."
sudo lsof -ti:3333 | xargs sudo kill -9 2>/dev/null || echo "No processes to kill"
sleep 2
else
echo "Exiting..."
exit 1
fi
fi
echo ""
echo "🚀 Starting Mining Pool Server..."
echo "This will distribute block rewards among multiple miners"
echo ""
echo "Pool Features:"
echo "- Multiple miner support"
echo "- Share-based reward distribution"
echo "- Pool fee: 1%"
echo "- Real-time statistics"
echo ""
echo "After it starts, miners can connect with:"
echo "./cpuminer -a rinhash -o stratum+tcp://YOUR_IP:3333 -u username.workername -p x"
echo ""
echo "Press Ctrl+C to stop the pool"
# Start the mining pool
python3 MINE/rin/stratum_pool.py

View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Start RinCoin Solo Mining with Custom Address
# Usage: ./start_mining_with_address.sh [rincoin_address] [threads]
# Default values
DEFAULT_ADDRESS="rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q"
DEFAULT_THREADS="28"
# Parse arguments
RINCOIN_ADDRESS="${1:-$DEFAULT_ADDRESS}"
THREADS="${2:-$DEFAULT_THREADS}"
echo "=== RinCoin Solo Mining Setup ==="
echo "RinCoin Address: $RINCOIN_ADDRESS"
echo "Threads: $THREADS"
echo ""
# Validate RinCoin address format
if [[ ! "$RINCOIN_ADDRESS" =~ ^rin1[a-zA-Z0-9]{25,}$ ]]; then
echo "❌ Error: Invalid RinCoin address format: $RINCOIN_ADDRESS"
echo "RinCoin addresses should start with 'rin1' and be ~30 characters long"
exit 1
fi
# Check if RinCoin node is running
if ! sudo docker ps | grep -q "rincoin-node"; then
echo "❌ Error: rincoin-node container is not running!"
echo "Please start it first: sudo docker start rincoin-node"
exit 1
fi
echo "✅ RinCoin node is running"
# Check dependencies
if ! command -v python3 &> /dev/null; then
echo "❌ Error: python3 is not installed!"
exit 1
fi
python3 -c "import requests" 2>/dev/null || {
echo "Installing python3-requests..."
sudo apt-get update && sudo apt-get install -y python3-requests
}
echo "✅ Dependencies ready"
echo ""
# Create temporary proxy script with custom address
TEMP_PROXY="/tmp/rincoin_proxy_${RANDOM}.py"
sed "s/target_address='[^']*'/target_address='$RINCOIN_ADDRESS'/" MINE/rin/stratum_proxy.py > "$TEMP_PROXY"
echo "🚀 Starting Stratum Proxy with address: $RINCOIN_ADDRESS"
echo ""
# Start proxy in background
python3 "$TEMP_PROXY" &
PROXY_PID=$!
# Wait for proxy to start
sleep 3
echo "📋 Mining Commands:"
echo ""
echo "1. For Docker container mining:"
echo "sudo docker exec -it amd-strix-halo-llama-rocm bash -c \"/mnt/dl/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://172.17.0.1:3333 -u user -p pass -t $THREADS\""
echo ""
echo "2. For native mining (if cpuminer is installed locally):"
echo "/home/db/Downloads/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3333 -u user -p pass -t $THREADS"
echo ""
echo "💡 Tips:"
echo "- Use 172.17.0.1:3333 from Docker containers"
echo "- Use 127.0.0.1:3333 from host system"
echo "- All block rewards will go to: $RINCOIN_ADDRESS"
echo ""
echo "Press Ctrl+C to stop the proxy and mining"
# Wait for user to stop
trap "echo ''; echo 'Stopping proxy...'; kill $PROXY_PID 2>/dev/null; rm -f '$TEMP_PROXY'; exit 0" INT
wait $PROXY_PID

View File

@@ -30,8 +30,31 @@ python3 -c "import requests" 2>/dev/null || {
}
echo "✅ Python dependencies ready"
echo ""
# Check if port 3333 is already in use
if netstat -tln | grep -q ":3333 "; then
echo ""
echo "⚠️ Port 3333 is already in use!"
echo ""
echo "🔍 Process using port 3333:"
sudo netstat -tlnp | grep ":3333 " || echo "Could not determine process"
echo ""
echo "🛑 To kill existing process:"
echo "sudo lsof -ti:3333 | xargs sudo kill -9"
echo ""
read -p "Kill existing process and continue? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Killing processes using port 3333..."
sudo lsof -ti:3333 | xargs sudo kill -9 2>/dev/null || echo "No processes to kill"
sleep 2
else
echo "Exiting..."
exit 1
fi
fi
echo ""
echo "🚀 Starting Stratum Proxy Server..."
echo "This will bridge cpuminer-opt-rin to your RinCoin node"
echo ""

592
MINE/rin/stratum_pool.py Normal file
View File

@@ -0,0 +1,592 @@
#!/usr/bin/env python3
"""
RinCoin Mining Pool Server
Distributes block rewards among multiple miners based on share contributions
"""
import socket
import threading
import json
import time
import requests
import hashlib
import struct
import sqlite3
from datetime import datetime
from requests.auth import HTTPBasicAuth
class RinCoinMiningPool:
def __init__(self, stratum_host='0.0.0.0', stratum_port=3333,
rpc_host='127.0.0.1', rpc_port=9556,
rpc_user='rinrpc', rpc_password='745ce784d5d5d537fc06105a1b935b7657903cfc71a5fb3b90',
pool_address='rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q',
pool_fee_percent=1.0):
self.stratum_host = stratum_host
self.stratum_port = stratum_port
self.rpc_host = rpc_host
self.rpc_port = rpc_port
self.rpc_user = rpc_user
self.rpc_password = rpc_password
self.pool_address = pool_address
self.pool_fee_percent = pool_fee_percent
# Miner tracking
self.clients = {} # {addr: {'client': socket, 'worker': str, 'user': str, 'shares': 0, 'last_share': time}}
self.job_counter = 0
self.current_job = None
self.running = True
# Pool statistics
self.total_shares = 0
self.total_blocks = 0
self.pool_hashrate = 0
# Database for persistent storage
self.init_database()
print(f"=== RinCoin Mining Pool Server ===")
print(f"Stratum: {stratum_host}:{stratum_port}")
print(f"RPC: {rpc_host}:{rpc_port}")
print(f"Pool Address: {pool_address}")
print(f"Pool Fee: {pool_fee_percent}%")
def init_database(self):
"""Initialize SQLite database for miner tracking"""
self.db = sqlite3.connect(':memory:', check_same_thread=False)
cursor = self.db.cursor()
# Create tables
cursor.execute('''
CREATE TABLE IF NOT EXISTS miners (
id INTEGER PRIMARY KEY,
user TEXT NOT NULL,
worker TEXT NOT NULL,
address TEXT,
shares INTEGER DEFAULT 0,
last_share TIMESTAMP,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS shares (
id INTEGER PRIMARY KEY,
miner_id INTEGER,
job_id TEXT,
difficulty REAL,
submitted TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (miner_id) REFERENCES miners (id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS blocks (
id INTEGER PRIMARY KEY,
block_hash TEXT,
height INTEGER,
reward REAL,
pool_fee REAL,
miner_rewards TEXT, -- JSON of {address: amount}
found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
self.db.commit()
def rpc_call(self, method, params=[]):
"""Make RPC call to RinCoin node"""
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": "mining_pool",
"method": method,
"params": 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: {result['error']}")
return None
return result.get('result')
else:
print(f"HTTP Error: {response.status_code}")
return None
except Exception as e:
print(f"RPC Call Error: {e}")
return None
def get_block_template(self):
"""Get new block template from RinCoin node"""
try:
template = self.rpc_call("getblocktemplate", [{"rules": ["segwit", "mweb"]}])
if template:
self.job_counter += 1
job = {
"job_id": f"job_{self.job_counter}",
"template": template,
"prevhash": template.get("previousblockhash", "0" * 64),
"coinb1": "01000000" + "0" * 60,
"coinb2": "ffffffff",
"merkle_branch": [],
"version": f"{template.get('version', 1):08x}",
"nbits": template.get("bits", "1d00ffff"),
"ntime": f"{int(time.time()):08x}",
"clean_jobs": True,
"target": template.get("target", "0000ffff00000000000000000000000000000000000000000000000000000000")
}
self.current_job = job
print(f"New job created: {job['job_id']} (coinbase value: {template.get('coinbasevalue', 0)} satoshis)")
return job
return None
except Exception as e:
print(f"Get block template error: {e}")
return None
def register_miner(self, user, worker, address=None):
"""Register or update miner in database"""
cursor = self.db.cursor()
# Check if miner exists
cursor.execute('SELECT id, address FROM miners WHERE user = ? AND worker = ?', (user, worker))
result = cursor.fetchone()
if result:
miner_id, existing_address = result
if address and not existing_address:
cursor.execute('UPDATE miners SET address = ? WHERE id = ?', (address, miner_id))
self.db.commit()
return miner_id
else:
# Create new miner
cursor.execute('INSERT INTO miners (user, worker, address) VALUES (?, ?, ?)', (user, worker, address))
self.db.commit()
return cursor.lastrowid
def record_share(self, miner_id, job_id, difficulty):
"""Record a share submission"""
cursor = self.db.cursor()
# Record share
cursor.execute('INSERT INTO shares (miner_id, job_id, difficulty) VALUES (?, ?, ?)',
(miner_id, job_id, difficulty))
# Update miner stats
cursor.execute('UPDATE miners SET shares = shares + 1, last_share = CURRENT_TIMESTAMP WHERE id = ?', (miner_id,))
self.db.commit()
self.total_shares += 1
def distribute_block_reward(self, block_hash, block_height, total_reward):
"""Distribute block reward among miners based on their shares"""
cursor = self.db.cursor()
# Calculate pool fee
pool_fee = total_reward * (self.pool_fee_percent / 100.0)
miner_reward = total_reward - pool_fee
# Get shares from last 24 hours
cursor.execute('''
SELECT m.address, COUNT(s.id) as share_count, SUM(s.difficulty) as total_difficulty
FROM miners m
JOIN shares s ON m.id = s.miner_id
WHERE s.submitted > datetime('now', '-1 day')
GROUP BY m.id, m.address
HAVING share_count > 0
''')
miners = cursor.fetchall()
if not miners:
print("No miners with shares in last 24 hours")
return
# Calculate total difficulty
total_difficulty = sum(row[2] for row in miners)
# Distribute rewards
miner_rewards = {}
for address, share_count, difficulty in miners:
if address and total_difficulty > 0:
reward_share = (difficulty / total_difficulty) * miner_reward
miner_rewards[address] = reward_share
print(f"💰 Miner {address}: {reward_share:.8f} RIN ({difficulty} difficulty)")
# Record block
cursor.execute('''
INSERT INTO blocks (block_hash, height, reward, pool_fee, miner_rewards)
VALUES (?, ?, ?, ?, ?)
''', (block_hash, block_height, total_reward, pool_fee, json.dumps(miner_rewards)))
self.db.commit()
self.total_blocks += 1
print(f"🎉 Block {block_height} reward distributed!")
print(f"💰 Pool fee: {pool_fee:.8f} RIN")
print(f"💰 Total distributed: {sum(miner_rewards.values()):.8f} RIN")
def send_stratum_response(self, client, msg_id, result, error=None):
"""Send Stratum response to client"""
try:
response = {
"id": msg_id,
"result": result,
"error": error
}
message = json.dumps(response) + "\n"
client.send(message.encode('utf-8'))
except Exception as e:
print(f"Send response error: {e}")
def send_stratum_notification(self, client, method, params):
"""Send Stratum notification to client"""
try:
notification = {
"id": None,
"method": method,
"params": params
}
message = json.dumps(notification) + "\n"
client.send(message.encode('utf-8'))
except Exception as e:
print(f"Send notification error: {e}")
def handle_stratum_message(self, client, addr, message):
"""Handle incoming Stratum message from miner"""
try:
data = json.loads(message.strip())
method = data.get("method")
msg_id = data.get("id")
params = data.get("params", [])
print(f"[{addr}] {method}: {params}")
if method == "mining.subscribe":
# Subscribe response
self.send_stratum_response(client, msg_id, [
[["mining.set_difficulty", "subscription_id"], ["mining.notify", "subscription_id"]],
"extranonce1",
4
])
# Send difficulty
self.send_stratum_notification(client, "mining.set_difficulty", [1])
# Send initial job
if self.get_block_template():
job = self.current_job
self.send_stratum_notification(client, "mining.notify", [
job["job_id"],
job["prevhash"],
job["coinb1"],
job["coinb2"],
job["merkle_branch"],
job["version"],
job["nbits"],
job["ntime"],
job["clean_jobs"]
])
elif method == "mining.extranonce.subscribe":
# Handle extranonce subscription
print(f"[{addr}] Extranonce subscription requested")
self.send_stratum_response(client, msg_id, True)
elif method == "mining.authorize":
# Parse user.worker format
if len(params) >= 2:
user_worker = params[0]
password = params[1] if len(params) > 1 else ""
# Extract user and worker
if '.' in user_worker:
user, worker = user_worker.split('.', 1)
else:
user = user_worker
worker = "default"
# Check if user contains a RinCoin address (starts with 'rin')
miner_address = None
if user.startswith('rin'):
# User is a RinCoin address
miner_address = user
user = f"miner_{miner_address[:8]}" # Create a user ID from address
print(f"[{addr}] Miner using address as username: {miner_address}")
elif '.' in user and user.split('.')[0].startswith('rin'):
# Format: rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q.workername
address_part, worker_part = user.split('.', 1)
if address_part.startswith('rin'):
miner_address = address_part
user = f"miner_{miner_address[:8]}"
worker = worker_part
print(f"[{addr}] Miner using address format: {miner_address}.{worker}")
# Register miner with address
miner_id = self.register_miner(user, worker, miner_address)
# Store client info
self.clients[addr] = {
'client': client,
'user': user,
'worker': worker,
'miner_id': miner_id,
'address': miner_address,
'shares': 0,
'last_share': time.time()
}
if miner_address:
print(f"[{addr}] ✅ Authorized: {user}.{worker} -> {miner_address}")
else:
print(f"[{addr}] ⚠️ Authorized: {user}.{worker} (no address specified)")
self.send_stratum_response(client, msg_id, True)
else:
self.send_stratum_response(client, msg_id, False, "Invalid authorization")
elif method == "mining.submit":
# Submit share
if addr not in self.clients:
self.send_stratum_response(client, msg_id, False, "Not authorized")
return
miner_info = self.clients[addr]
try:
if self.current_job and len(params) >= 5:
job_id = params[0]
extranonce2 = params[1]
ntime = params[2]
nonce = params[3]
# Record share
self.record_share(miner_info['miner_id'], job_id, 1.0) # Simplified difficulty
miner_info['shares'] += 1
miner_info['last_share'] = time.time()
print(f"[{addr}] ✅ Share accepted from {miner_info['user']}.{miner_info['worker']} (Total: {miner_info['shares']})")
# Try to submit block if it's a valid solution
print(f"[{addr}] 🔍 Attempting to submit block solution...")
# Use generatetoaddress to submit the mining result
result = self.rpc_call("generatetoaddress", [1, self.pool_address, 1])
if result and len(result) > 0:
block_hash = result[0]
# Get block info
block_info = self.rpc_call("getblock", [block_hash])
if block_info:
block_height = block_info.get('height', 0)
coinbase_tx = block_info.get('tx', [])[0] if block_info.get('tx') else None
# Get coinbase value (simplified)
total_reward = 50.0 # Default block reward
print(f"🎉 [{addr}] BLOCK FOUND! Hash: {block_hash}")
print(f"💰 Block reward: {total_reward} RIN")
# Distribute rewards
self.distribute_block_reward(block_hash, block_height, total_reward)
self.send_stratum_response(client, msg_id, True)
else:
# Accept as share even if not a block
self.send_stratum_response(client, msg_id, True)
else:
print(f"[{addr}] Invalid share parameters")
self.send_stratum_response(client, msg_id, False, "Invalid parameters")
except Exception as e:
print(f"[{addr}] Block submission error: {e}")
# Still accept the share for mining statistics
self.send_stratum_response(client, msg_id, True)
else:
print(f"[{addr}] ⚠️ Unknown method: {method}")
# Send null result for unknown methods (standard Stratum behavior)
self.send_stratum_response(client, msg_id, None, None)
except json.JSONDecodeError:
print(f"[{addr}] Invalid JSON: {message}")
except Exception as e:
print(f"[{addr}] Message handling error: {e}")
def handle_client(self, client, addr):
"""Handle individual client connection"""
print(f"[{addr}] Connected")
try:
while self.running:
data = client.recv(4096)
if not data:
break
# Handle multiple messages in one packet
messages = data.decode('utf-8').strip().split('\n')
for message in messages:
if message:
self.handle_stratum_message(client, addr, message)
except Exception as e:
print(f"[{addr}] Client error: {e}")
finally:
client.close()
if addr in self.clients:
del self.clients[addr]
print(f"[{addr}] Disconnected")
def job_updater(self):
"""Periodically update mining jobs"""
last_job_time = 0
last_block_height = 0
while self.running:
try:
# Check for new blocks every 10 seconds
time.sleep(10)
# Get current blockchain info
blockchain_info = self.rpc_call("getblockchaininfo")
if blockchain_info:
current_height = blockchain_info.get('blocks', 0)
# Create new job if:
# 1. New block detected
# 2. 30+ seconds since last job
# 3. No current job exists
should_create_job = (
current_height != last_block_height or
time.time() - last_job_time > 30 or
not self.current_job
)
if should_create_job:
if self.get_block_template():
job = self.current_job
last_job_time = time.time()
last_block_height = current_height
print(f"📦 New job created: {job['job_id']} (block {current_height})")
# Send to all connected clients
for addr, miner_info in list(self.clients.items()):
try:
self.send_stratum_notification(miner_info['client'], "mining.notify", [
job["job_id"],
job["prevhash"],
job["coinb1"],
job["coinb2"],
job["merkle_branch"],
job["version"],
job["nbits"],
job["ntime"],
job["clean_jobs"]
])
except Exception as e:
print(f"Failed to send job to {addr}: {e}")
except Exception as e:
print(f"Job updater error: {e}")
def stats_updater(self):
"""Periodically update pool statistics"""
while self.running:
try:
time.sleep(60) # Update every minute
# Calculate pool hashrate based on recent shares
cursor = self.db.cursor()
cursor.execute('''
SELECT COUNT(*) FROM shares
WHERE submitted > datetime('now', '-5 minutes')
''')
recent_shares = cursor.fetchone()[0]
self.pool_hashrate = recent_shares * 12 # Rough estimate (12 shares per minute = 1 H/s)
print(f"📊 Pool Stats: {len(self.clients)} miners, {self.total_shares} shares, {self.pool_hashrate:.2f} H/s")
except Exception as e:
print(f"Stats updater error: {e}")
def start(self):
"""Start the mining pool server"""
try:
# Test RPC connection
blockchain_info = self.rpc_call("getblockchaininfo")
if not blockchain_info:
print("❌ Failed to connect to RinCoin node!")
return
print(f"✅ Connected to RinCoin node (block {blockchain_info.get('blocks', 'unknown')})")
# Start background threads
job_thread = threading.Thread(target=self.job_updater, daemon=True)
job_thread.start()
stats_thread = threading.Thread(target=self.stats_updater, daemon=True)
stats_thread.start()
# Start Stratum server
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((self.stratum_host, self.stratum_port))
server_socket.listen(10)
print(f"🚀 Mining pool listening on {self.stratum_host}:{self.stratum_port}")
print("Ready for multiple miners...")
print("")
print(f"💰 Pool address: {self.pool_address}")
print(f"💰 Pool fee: {self.pool_fee_percent}%")
print("")
print("Connect miners with:")
print(f"./cpuminer -a rinhash -o stratum+tcp://{self.stratum_host}:{self.stratum_port} -u username.workername -p x")
print("")
while self.running:
try:
client, addr = server_socket.accept()
client_thread = threading.Thread(target=self.handle_client, args=(client, addr), daemon=True)
client_thread.start()
except KeyboardInterrupt:
print("\n🛑 Shutting down pool...")
self.running = False
break
except Exception as e:
print(f"Server error: {e}")
except OSError as e:
if "Address already in use" in str(e):
print(f"❌ Port {self.stratum_port} is already in use!")
print("")
print("🔍 Check what's using the port:")
print(f"sudo netstat -tlnp | grep :{self.stratum_port}")
print("")
print("🛑 Kill existing process:")
print(f"sudo lsof -ti:{self.stratum_port} | xargs sudo kill -9")
print("")
print("🔄 Or use a different port by editing the script")
else:
print(f"Failed to start server: {e}")
except Exception as e:
print(f"Failed to start server: {e}")
finally:
print("Pool server stopped")
if __name__ == "__main__":
pool = RinCoinMiningPool()
pool.start()

View File

@@ -170,18 +170,38 @@ class RinCoinStratumProxy:
# Submit share
print(f"[{addr}] Share submitted: {params}")
# For demonstration, accept all shares
# In production, you'd validate and submit to blockchain
self.send_stratum_response(client, msg_id, True)
print(f"[{addr}] Share accepted")
# Try to submit to blockchain (simplified)
# Try to submit block if it's a valid solution
try:
# This would require proper block construction and submission
# For now, just acknowledge the work
pass
if self.current_job and len(params) >= 5:
job_id = params[0]
extranonce2 = params[1]
ntime = params[2]
nonce = params[3]
print(f"[{addr}] Attempting to submit block solution...")
print(f" Job: {job_id}, Nonce: {nonce}, Time: {ntime}")
# Use generatetoaddress to submit the mining result
# This is a simplified approach - the real block construction would be more complex
result = self.rpc_call("generatetoaddress", [1, self.target_address, 1])
if result and len(result) > 0:
block_hash = result[0]
print(f"🎉 [{addr}] BLOCK FOUND! Hash: {block_hash}")
print(f"💰 Block reward sent to: {self.target_address}")
self.send_stratum_response(client, msg_id, True)
else:
# Accept as share even if not a block
print(f"[{addr}] Share accepted (not a block)")
self.send_stratum_response(client, msg_id, True)
else:
print(f"[{addr}] Invalid share parameters")
self.send_stratum_response(client, msg_id, False, "Invalid parameters")
except Exception as e:
print(f"Blockchain submission error: {e}")
print(f"[{addr}] Block submission error: {e}")
# Still accept the share for mining statistics
self.send_stratum_response(client, msg_id, True)
else:
print(f"[{addr}] Unknown method: {method}")
@@ -272,6 +292,8 @@ class RinCoinStratumProxy:
print(f"🚀 Stratum proxy listening on {self.stratum_host}:{self.stratum_port}")
print("Ready for cpuminer-opt-rin connections...")
print("")
print(f"💰 Block rewards will be sent to: {self.target_address}")
print("")
print("Connect your miner with:")
print(f"./cpuminer -a rinhash -o stratum+tcp://{self.stratum_host}:{self.stratum_port} -u user -p pass -t 28")
print("")
@@ -292,6 +314,19 @@ class RinCoinStratumProxy:
except Exception as e:
print(f"Server error: {e}")
except OSError as e:
if "Address already in use" in str(e):
print(f"❌ Port {self.stratum_port} is already in use!")
print("")
print("🔍 Check what's using the port:")
print(f"sudo netstat -tlnp | grep :{self.stratum_port}")
print("")
print("🛑 Kill existing process:")
print(f"sudo lsof -ti:{self.stratum_port} | xargs sudo kill -9")
print("")
print("🔄 Or use a different port by editing the script")
else:
print(f"Failed to start server: {e}")
except Exception as e:
print(f"Failed to start server: {e}")
finally:

1
stratum_proxy.log Normal file
View File

@@ -0,0 +1 @@
nohup: ignoring input