From d18906f1b18375a035edff6dce5577870f8ecaf2 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Mon, 15 Sep 2025 22:58:27 +0300 Subject: [PATCH] fix name --- rin/proxy/README.md | 12 +- rin/proxy/custom/start_fixed_proxy.sh | 4 +- rin/proxy/custom/start_stratum_proxy.sh | 68 ++ rin/proxy/custom/stratum_proxy.py | 159 ++--- rin/proxy/custom/stratum_proxy_fixed.py | 642 ------------------- rin/proxy/custom/stratum_proxy_original.py | 693 --------------------- 6 files changed, 130 insertions(+), 1448 deletions(-) create mode 100644 rin/proxy/custom/start_stratum_proxy.sh delete mode 100644 rin/proxy/custom/stratum_proxy_fixed.py delete mode 100644 rin/proxy/custom/stratum_proxy_original.py diff --git a/rin/proxy/README.md b/rin/proxy/README.md index 89c7f57..7988a6a 100644 --- a/rin/proxy/README.md +++ b/rin/proxy/README.md @@ -63,20 +63,20 @@ The original code didn't show the actual hash vs target comparison, making it di ``` /mnt/shared/DEV/repos/d-popov.com/mines/rin/proxy/ ├── custom/ # Our implementation -│ ├── stratum_proxy_fixed.py # Fixed version with all issues resolved -│ ├── stratum_proxy_original.py # Original version for comparison -│ └── start_fixed_proxy.sh # Startup script for fixed version +│ ├── stratum_proxy.py # Fixed version with all issues resolved +│ └── start_stratum_proxy.sh # Startup script ├── third-party/ # External stratum implementations │ └── [stratum library files] # Node.js stratum implementation -└── README.md # This file +├── README.md # This file +└── CRITICAL_FIXES.md # Detailed explanation of fixes ``` ## Usage -### 1. Start Fixed Stratum Proxy +### 1. Start Stratum Proxy ```bash cd /mnt/shared/DEV/repos/d-popov.com/mines/rin/proxy/custom -./start_fixed_proxy.sh +./start_stratum_proxy.sh ``` ### 2. Connect Miner diff --git a/rin/proxy/custom/start_fixed_proxy.sh b/rin/proxy/custom/start_fixed_proxy.sh index 3121c3f..7857d55 100644 --- a/rin/proxy/custom/start_fixed_proxy.sh +++ b/rin/proxy/custom/start_fixed_proxy.sh @@ -72,6 +72,6 @@ echo "" echo "Press Ctrl+C to stop the proxy" echo "" -# Start the fixed proxy +# Start the proxy cd "$(dirname "$0")" -python3 stratum_proxy_fixed.py +python3 stratum_proxy.py diff --git a/rin/proxy/custom/start_stratum_proxy.sh b/rin/proxy/custom/start_stratum_proxy.sh new file mode 100644 index 0000000..4bf852c --- /dev/null +++ b/rin/proxy/custom/start_stratum_proxy.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Start RinCoin Stratum Proxy Server +# Bridges cpuminer-opt-rin to RinCoin node + +echo "=== RinCoin Stratum Proxy Server ===" +echo "" + +# 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:" + echo "sudo docker start rincoin-node" + exit 1 +fi + +echo "✅ RinCoin node is running" + +# Check if Python3 and requests are available +if ! command -v python3 &> /dev/null; then + echo "❌ Error: python3 is not installed!" + echo "Please install it: sudo apt-get install python3" + exit 1 +fi + +# Install requests if not available +python3 -c "import requests" 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 3334 is already in use +if netstat -tln | grep -q ":3334 "; then + echo "" + echo "⚠️ Port 3334 is already in use!" + echo "" + echo "🔍 Process using port 3334:" + sudo netstat -tlnp | grep ":3334 " || echo "Could not determine process" + echo "" + echo "🛑 To kill existing process:" + echo "sudo lsof -ti:3334 | 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 3334..." + sudo lsof -ti:3334 | 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 "" +echo "After it starts, connect your miner with:" +echo "sudo docker exec -it amd-strix-halo-llama-rocm bash -c \"/mnt/dl/rinhash/cpuminer-opt-rin/cpuminer -a rinhash -o stratum+tcp://127.0.0.1:3334 -u user -p pass -t 28\"" +echo "" +echo "Press Ctrl+C to stop the proxy" +echo "" + +# Start the proxy +cd "$(dirname "$0")" +python3 stratum_proxy.py diff --git a/rin/proxy/custom/stratum_proxy.py b/rin/proxy/custom/stratum_proxy.py index d677f8a..c605f1a 100644 --- a/rin/proxy/custom/stratum_proxy.py +++ b/rin/proxy/custom/stratum_proxy.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -RinCoin Stratum Proxy Server - REAL MINING VERSION -Properly constructs Stratum jobs and validates/submits real blocks +RinCoin Stratum Proxy Server - FIXED VERSION +Fixed block hash calculation and validation issues """ import socket @@ -13,7 +13,7 @@ import hashlib import struct from requests.auth import HTTPBasicAuth -class RinCoinStratumBase: +class RinCoinStratumProxy: def __init__(self, stratum_host='0.0.0.0', stratum_port=3334, rpc_host='127.0.0.1', rpc_port=9556, rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90', @@ -33,7 +33,7 @@ class RinCoinStratumBase: self.running = True self.extranonce1_counter = 0 - print(f"RinCoin Stratum Proxy Server - REAL MINING") + print(f"RinCoin Stratum Proxy Server") print(f"Stratum: {stratum_host}:{stratum_port}") print(f"RPC: {rpc_host}:{rpc_port}") print(f"Target: {target_address}") @@ -196,14 +196,21 @@ class RinCoinStratumBase: return b'\x00' * 32 def bits_to_target(self, bits_hex): - """Convert bits to target""" + """Convert bits to target - FIXED VERSION""" try: bits = int(bits_hex, 16) exponent = bits >> 24 mantissa = bits & 0xffffff - target = mantissa * (256 ** (exponent - 3)) + + # Bitcoin target calculation + if exponent <= 3: + target = mantissa >> (8 * (3 - exponent)) + else: + target = mantissa << (8 * (exponent - 3)) + return f"{target:064x}" - except: + except Exception as e: + print(f"Bits to target error: {e}") return "0000ffff00000000000000000000000000000000000000000000000000000000" def get_block_template(self): @@ -241,21 +248,19 @@ class RinCoinStratumBase: return None def calculate_share_difficulty(self, hash_hex, target_hex): - """Calculate actual share difficulty from hash""" + """Calculate actual share difficulty from hash - FIXED""" try: hash_int = int(hash_hex, 16) - target_int = int(target_hex, 16) if hash_int == 0: return float('inf') # Perfect hash - # Bitcoin-style difficulty calculation - # Lower hash = higher difficulty - # Difficulty 1.0 = finding hash that meets network target exactly - max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 + # Bitcoin-style difficulty calculation using difficulty 1 target + # Difficulty 1 target for mainnet + diff1_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - # Share difficulty = how hard this specific hash was to find - difficulty = max_target / hash_int + # Share difficulty = how much harder this hash was compared to diff 1 + difficulty = diff1_target / hash_int return difficulty except Exception as e: @@ -263,15 +268,15 @@ class RinCoinStratumBase: return 0.0 def calculate_network_difficulty(self, target_hex): - """Calculate network difficulty from target""" + """Calculate network difficulty from target - FIXED""" try: target_int = int(target_hex, 16) # Bitcoin difficulty 1.0 target - max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 + diff1_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 # Network difficulty = how much harder than difficulty 1.0 - network_difficulty = max_target / target_int + network_difficulty = diff1_target / target_int return network_difficulty except Exception as e: @@ -279,7 +284,7 @@ class RinCoinStratumBase: return 1.0 def submit_share(self, job, extranonce1, extranonce2, ntime, nonce, target_address=None): - """Validate share and submit block if valid""" + """Validate share and submit block if valid - FIXED VERSION""" try: # Use provided address or default address = target_address or self.target_address @@ -296,33 +301,34 @@ class RinCoinStratumBase: # Calculate merkle root merkle_root = self.calculate_merkle_root(coinbase_txid, job['transactions']) - # Build block header + # Build block header - FIXED ENDIANNESS header = b'' - header += struct.pack(' 0 else 0 # Progress indicator based on percentage - if difficulty_percentage >= 100: + if meets_target: progress_icon = "🎉" # Block found! elif difficulty_percentage >= 50: progress_icon = "🔥" # Very close @@ -338,37 +344,12 @@ class RinCoinStratumBase: print(f" 📈 Progress: {difficulty_percentage:.4f}% of network difficulty") print(f" 📍 Target: {job['target'][:16]}... | Height: {job['height']}") print(f" ⏰ Time: {ntime} | Extranonce: {extranonce1}:{extranonce2}") + print(f" 🔍 Hash vs Target: {hash_int} {'<=' if meets_target else '>'} {target_int}") - if hash_int > target_int: - # Valid share but not a block - still send to node for validation - print(f" ✅ Share accepted (below network difficulty)") - - # Send to node anyway to validate our work - try: - # Build complete block for validation - block = header - tx_count = 1 + len(job['transactions']) - block += self.encode_varint(tx_count) - block += coinbase_wit - for tx in job['transactions']: - block += bytes.fromhex(tx['data']) - - block_hex = block.hex() - print(f" 🔍 Sending share to node for validation...") - result = self.rpc_call("submitblock", [block_hex]) - - if result is None: - print(f" 🎉 SURPRISE BLOCK! Node accepted our 'low difficulty' share as valid block!") - return True, "Block found and submitted" - else: - print(f" 📊 Node rejected as expected: {result}") - return True, "Share validated by node" - - except Exception as e: - print(f" ⚠️ Node validation error: {e}") - return True, "Share accepted (node validation failed)" - - return True, "Share accepted" + if not meets_target: + # Share doesn't meet target - reject but still useful for debugging + print(f" ❌ Share rejected (hash > target)") + return False, "Share too high" # Valid block! Build full block and submit print(f" 🎉 BLOCK FOUND! Hash: {block_hash_hex}") @@ -407,10 +388,6 @@ class RinCoinStratumBase: except Exception as e: print(f"Share submission error: {e}") return False, f"Submission error: {e}" - - except Exception as e: - print(f"Share submission error: {e}") - return False, f"Submission error: {e}" def send_stratum_response(self, client, msg_id, result, error=None): """Send Stratum response to client""" @@ -463,8 +440,8 @@ class RinCoinStratumBase: 4 # extranonce2 size ]) - # Send difficulty - self.send_stratum_notification(client, "mining.set_difficulty", [0.0001]) + # Send difficulty - MUCH LOWER for testing + self.send_stratum_notification(client, "mining.set_difficulty", [0.00001]) # Send initial job if self.current_job: @@ -481,7 +458,6 @@ class RinCoinStratumBase: print(f"[{timestamp}] 🔐 [{addr}] Authorized as {username}") elif method == "mining.extranonce.subscribe": - # Handle extranonce subscription self.send_stratum_response(client, msg_id, True) elif method == "mining.submit": @@ -494,38 +470,21 @@ class RinCoinStratumBase: print(f"[{addr}] Submit: {username} | job={job_id} | nonce={nonce}") - # Validate submission - if self.current_job and job_id == self.current_job['job_id']: + # Always validate against current job + if self.current_job: extranonce1 = self.clients[addr].get('extranonce1', '00000000') # Submit share success, message = self.submit_share(self.current_job, extranonce1, extranonce2, ntime, nonce) - if success: - self.send_stratum_response(client, msg_id, True) - if "Block found" in message: - # Get new job after block found - threading.Thread(target=self.update_job_after_block, daemon=True).start() - else: - self.send_stratum_response(client, msg_id, False, message) + # Always accept shares for debugging, even if they don't meet target + self.send_stratum_response(client, msg_id, True) + + if success and "Block found" in message: + # Get new job after block found + threading.Thread(target=self.update_job_after_block, daemon=True).start() else: - # For stale jobs, still validate for blocks but don't require exact job match - # This prevents missing blocks due to job timing issues - if self.current_job: - extranonce1 = self.clients[addr].get('extranonce1', '00000000') - # Use current job template but allow stale job_id - success, message = self.submit_share(self.current_job, extranonce1, extranonce2, ntime, nonce) - - if success: - self.send_stratum_response(client, msg_id, True) - if "Block found" in message: - # Get new job after block found - threading.Thread(target=self.update_job_after_block, daemon=True).start() - else: - # Accept as share even if block validation fails for stale jobs - self.send_stratum_response(client, msg_id, True) - else: - self.send_stratum_response(client, msg_id, True) + self.send_stratum_response(client, msg_id, True) else: self.send_stratum_response(client, msg_id, False, "Invalid parameters") @@ -541,7 +500,6 @@ class RinCoinStratumBase: def send_job_to_client(self, client, job): """Send mining job to specific client""" try: - # Send proper Stratum job self.send_stratum_notification(client, "mining.notify", [ job["job_id"], job["prevhash"], @@ -649,7 +607,7 @@ class RinCoinStratumBase: server_socket.listen(10) timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - print(f"[{timestamp}] 🚀 REAL Mining Stratum proxy ready!") + print(f"[{timestamp}] 🚀 Mining Stratum proxy ready!") print(f" 📡 Listening on {self.stratum_host}:{self.stratum_port}") print(f" 💰 Mining to: {self.target_address}") print(f" 📊 Current job: {self.current_job['job_id'] if self.current_job else 'None'}") @@ -679,15 +637,6 @@ class RinCoinStratumBase: finally: print("Server stopped") -class RinCoinStratumProxy(RinCoinStratumBase): - """Solo mining stratum proxy implementation""" - - def __init__(self, stratum_host='0.0.0.0', stratum_port=3334, - rpc_host='127.0.0.1', rpc_port=9556, - rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90', - target_address='rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q'): - super().__init__(stratum_host, stratum_port, rpc_host, rpc_port, rpc_user, rpc_password, target_address) - if __name__ == "__main__": proxy = RinCoinStratumProxy() - proxy.start() \ No newline at end of file + proxy.start() diff --git a/rin/proxy/custom/stratum_proxy_fixed.py b/rin/proxy/custom/stratum_proxy_fixed.py deleted file mode 100644 index acb193b..0000000 --- a/rin/proxy/custom/stratum_proxy_fixed.py +++ /dev/null @@ -1,642 +0,0 @@ -#!/usr/bin/env python3 -""" -RinCoin Stratum Proxy Server - FIXED VERSION -Fixed block hash calculation and validation issues -""" - -import socket -import threading -import json -import time -import requests -import hashlib -import struct -from requests.auth import HTTPBasicAuth - -class RinCoinStratumProxyFixed: - def __init__(self, stratum_host='0.0.0.0', stratum_port=3334, - rpc_host='127.0.0.1', rpc_port=9556, - rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90', - target_address='rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q'): - - 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.target_address = target_address - - self.clients = {} - self.job_counter = 0 - self.current_job = None - self.running = True - self.extranonce1_counter = 0 - - print(f"RinCoin Stratum Proxy Server - FIXED VERSION") - print(f"Stratum: {stratum_host}:{stratum_port}") - print(f"RPC: {rpc_host}:{rpc_port}") - print(f"Target: {target_address}") - - 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": "stratum_proxy", - "method": method, - "params": params - } - - response = requests.post(url, json=payload, headers=headers, auth=auth, timeout=30) - - 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 encode_varint(self, n): - """Encode integer as Bitcoin-style varint""" - if n < 0xfd: - return bytes([n]) - elif n <= 0xffff: - return b"\xfd" + struct.pack(' bytes: - outputs_blob = b'' - outputs_list = [] - # Main output - outputs_list.append(struct.pack(' 1: - if len(hashes) % 2 == 1: - hashes.append(hashes[-1]) # Duplicate last hash if odd - - next_level = [] - for i in range(0, len(hashes), 2): - combined = hashes[i] + hashes[i + 1] - next_level.append(hashlib.sha256(hashlib.sha256(combined).digest()).digest()) - - hashes = next_level - - return hashes[0] if hashes else b'\x00' * 32 - - except Exception as e: - print(f"Merkle root calculation error: {e}") - return b'\x00' * 32 - - def bits_to_target(self, bits_hex): - """Convert bits to target - FIXED VERSION""" - try: - bits = int(bits_hex, 16) - exponent = bits >> 24 - mantissa = bits & 0xffffff - - # Bitcoin target calculation - if exponent <= 3: - target = mantissa >> (8 * (3 - exponent)) - else: - target = mantissa << (8 * (exponent - 3)) - - return f"{target:064x}" - except Exception as e: - print(f"Bits to target error: {e}") - return "0000ffff00000000000000000000000000000000000000000000000000000000" - - def get_block_template(self): - """Get new block template and create Stratum job""" - try: - template = self.rpc_call("getblocktemplate", [{"rules": ["segwit", "mweb"]}]) - if not template: - return None - - self.job_counter += 1 - - job = { - "job_id": f"job_{self.job_counter:08x}", - "template": template, - "prevhash": template.get("previousblockhash", "0" * 64), - "version": template.get('version', 1), - "bits": template.get('bits', '1d00ffff'), - "ntime": f"{int(time.time()):08x}", - "target": self.bits_to_target(template.get('bits', '1d00ffff')), - "height": template.get('height', 0), - "coinbasevalue": template.get('coinbasevalue', 0), - "transactions": template.get('transactions', []) - } - - self.current_job = job - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - network_difficulty = self.calculate_network_difficulty(job['target']) - print(f"[{timestamp}] 🆕 NEW JOB: {job['job_id']} | Height: {job['height']} | Reward: {job['coinbasevalue']/100000000:.2f} RIN") - print(f" 🎯 Network Difficulty: {network_difficulty:.6f} | Bits: {job['bits']}") - print(f" 📍 Target: {job['target'][:16]}... | Transactions: {len(job['transactions'])}") - return job - - except Exception as e: - print(f"Get block template error: {e}") - return None - - def calculate_share_difficulty(self, hash_hex, target_hex): - """Calculate actual share difficulty from hash - FIXED""" - try: - hash_int = int(hash_hex, 16) - - if hash_int == 0: - return float('inf') # Perfect hash - - # Bitcoin-style difficulty calculation using difficulty 1 target - # Difficulty 1 target for mainnet - diff1_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - - # Share difficulty = how much harder this hash was compared to diff 1 - difficulty = diff1_target / hash_int - - return difficulty - except Exception as e: - print(f"Difficulty calculation error: {e}") - return 0.0 - - def calculate_network_difficulty(self, target_hex): - """Calculate network difficulty from target - FIXED""" - try: - target_int = int(target_hex, 16) - - # Bitcoin difficulty 1.0 target - diff1_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - - # Network difficulty = how much harder than difficulty 1.0 - network_difficulty = diff1_target / target_int - - return network_difficulty - except Exception as e: - print(f"Network difficulty calculation error: {e}") - return 1.0 - - def submit_share(self, job, extranonce1, extranonce2, ntime, nonce, target_address=None): - """Validate share and submit block if valid - FIXED VERSION""" - try: - # Use provided address or default - address = target_address or self.target_address - - # Build coinbase (with and without witness) - coinbase_wit, coinbase_nowit = self.build_coinbase_transaction_for_address( - job['template'], extranonce1, extranonce2, address) - if not coinbase_wit or not coinbase_nowit: - return False, "Coinbase construction failed" - - # Calculate coinbase txid (non-witness serialization) - coinbase_txid = hashlib.sha256(hashlib.sha256(coinbase_nowit).digest()).digest()[::-1] - - # Calculate merkle root - merkle_root = self.calculate_merkle_root(coinbase_txid, job['transactions']) - - # Build block header - FIXED ENDIANNESS - header = b'' - header += struct.pack(' 0 else 0 - - # Progress indicator based on percentage - if meets_target: - progress_icon = "🎉" # Block found! - elif difficulty_percentage >= 50: - progress_icon = "🔥" # Very close - elif difficulty_percentage >= 10: - progress_icon = "⚡" # Getting warm - elif difficulty_percentage >= 1: - progress_icon = "💫" # Some progress - else: - progress_icon = "📊" # Low progress - - print(f"[{timestamp}] {progress_icon} SHARE: job={job['job_id']} | nonce={nonce} | hash={block_hash_hex[:16]}...") - print(f" 🎯 Share Diff: {share_difficulty:.2e} | Network Diff: {network_difficulty:.6f}") - print(f" 📈 Progress: {difficulty_percentage:.4f}% of network difficulty") - print(f" 📍 Target: {job['target'][:16]}... | Height: {job['height']}") - print(f" ⏰ Time: {ntime} | Extranonce: {extranonce1}:{extranonce2}") - print(f" 🔍 Hash vs Target: {hash_int} {'<=' if meets_target else '>'} {target_int}") - - if not meets_target: - # Share doesn't meet target - reject but still useful for debugging - print(f" ❌ Share rejected (hash > target)") - return False, "Share too high" - - # Valid block! Build full block and submit - print(f" 🎉 BLOCK FOUND! Hash: {block_hash_hex}") - print(f" 💰 Reward: {job['coinbasevalue']/100000000:.2f} RIN -> {address}") - print(f" 📊 Block height: {job['height']}") - print(f" 🔍 Difficulty: {share_difficulty:.6f} (target: {network_difficulty:.6f})") - - # Build complete block - block = header - - # Transaction count - tx_count = 1 + len(job['transactions']) - block += self.encode_varint(tx_count) - - # Add coinbase transaction (witness variant for block body) - block += coinbase_wit - - # Add other transactions - for tx in job['transactions']: - block += bytes.fromhex(tx['data']) - - # Submit block - block_hex = block.hex() - print(f" 📦 Submitting block of size {len(block_hex)//2} bytes...") - - result = self.rpc_call("submitblock", [block_hex]) - - if result is None: - print(f" ✅ Block accepted by network!") - return True, "Block found and submitted" - else: - print(f" ❌ Block rejected: {result}") - print(f" 🔍 Debug: Block size {len(block_hex)//2} bytes, {len(job['transactions'])} transactions") - return False, f"Block rejected: {result}" - - except Exception as e: - print(f"Share submission error: {e}") - return False, f"Submission error: {e}" - - 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", []) - - if method == "mining.subscribe": - # Generate unique extranonce1 for this connection - self.extranonce1_counter += 1 - extranonce1 = f"{self.extranonce1_counter:08x}" - - # Store extranonce1 for this client - if addr not in self.clients: - self.clients[addr] = {} - self.clients[addr]['extranonce1'] = extranonce1 - - # Subscribe response - self.send_stratum_response(client, msg_id, [ - [["mining.set_difficulty", "subscription_id"], ["mining.notify", "subscription_id"]], - extranonce1, - 4 # extranonce2 size - ]) - - # Send difficulty - MUCH LOWER for testing - self.send_stratum_notification(client, "mining.set_difficulty", [0.00001]) - - # Send initial job - if self.current_job: - self.send_job_to_client(client, self.current_job) - else: - if self.get_block_template(): - self.send_job_to_client(client, self.current_job) - - elif method == "mining.authorize": - username = params[0] if params else "anonymous" - self.clients[addr]['username'] = username - self.send_stratum_response(client, msg_id, True) - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - print(f"[{timestamp}] 🔐 [{addr}] Authorized as {username}") - - elif method == "mining.extranonce.subscribe": - self.send_stratum_response(client, msg_id, True) - - elif method == "mining.submit": - if len(params) >= 5: - username = params[0] - job_id = params[1] - extranonce2 = params[2] - ntime = params[3] - nonce = params[4] - - print(f"[{addr}] Submit: {username} | job={job_id} | nonce={nonce}") - - # Always validate against current job - if self.current_job: - extranonce1 = self.clients[addr].get('extranonce1', '00000000') - - # Submit share - success, message = self.submit_share(self.current_job, extranonce1, extranonce2, ntime, nonce) - - # Always accept shares for debugging, even if they don't meet target - self.send_stratum_response(client, msg_id, True) - - if success and "Block found" in message: - # Get new job after block found - threading.Thread(target=self.update_job_after_block, daemon=True).start() - else: - self.send_stratum_response(client, msg_id, True) - else: - self.send_stratum_response(client, msg_id, False, "Invalid parameters") - - else: - print(f"[{addr}] Unknown method: {method}") - self.send_stratum_response(client, msg_id, None, "Unknown method") - - except json.JSONDecodeError: - print(f"[{addr}] Invalid JSON: {message}") - except Exception as e: - print(f"[{addr}] Message handling error: {e}") - - def send_job_to_client(self, client, job): - """Send mining job to specific client""" - try: - self.send_stratum_notification(client, "mining.notify", [ - job["job_id"], - job["prevhash"], - "", # coinb1 (empty for now - miner handles coinbase) - "", # coinb2 (empty for now - miner handles coinbase) - [], # merkle_branch (empty for now - we calculate merkle root) - f"{job['version']:08x}", - job["bits"], - job["ntime"], - True # clean_jobs - ]) - except Exception as e: - print(f"Failed to send job: {e}") - - def update_job_after_block(self): - """Update job after a block is found""" - time.sleep(2) # Brief delay to let network propagate - if self.get_block_template(): - self.broadcast_new_job() - - def broadcast_new_job(self): - """Broadcast new job to all connected clients""" - if not self.current_job: - return - - print(f"Broadcasting new job to {len(self.clients)} clients") - - for addr, client_data in list(self.clients.items()): - try: - if 'socket' in client_data: - self.send_job_to_client(client_data['socket'], self.current_job) - except Exception as e: - print(f"Failed to send job to {addr}: {e}") - - def handle_client(self, client, addr): - """Handle individual client connection""" - print(f"[{addr}] Connected") - if addr not in self.clients: - self.clients[addr] = {} - self.clients[addr]['socket'] = client - - 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""" - while self.running: - try: - time.sleep(30) # Update every 30 seconds - - old_height = self.current_job['height'] if self.current_job else 0 - - if self.get_block_template(): - new_height = self.current_job['height'] - if new_height > old_height: - print(f"New block detected! Broadcasting new job...") - self.broadcast_new_job() - - except Exception as e: - print(f"Job updater error: {e}") - - def start(self): - """Start the Stratum proxy 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") - print(f"Current height: {blockchain_info.get('blocks', 'unknown')}") - print(f"Chain: {blockchain_info.get('chain', 'unknown')}") - - # Get initial block template - if not self.get_block_template(): - print("Failed to get initial block template!") - return - - # Start job updater thread - job_thread = threading.Thread(target=self.job_updater, daemon=True) - job_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) - - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - print(f"[{timestamp}] 🚀 FIXED Mining Stratum proxy ready!") - print(f" 📡 Listening on {self.stratum_host}:{self.stratum_port}") - print(f" 💰 Mining to: {self.target_address}") - print(f" 📊 Current job: {self.current_job['job_id'] if self.current_job else 'None'}") - print("") - print(" 🔧 Miner command:") - print(f" ./cpuminer -a rinhash -o stratum+tcp://{self.stratum_host}:{self.stratum_port} -u worker1 -p x -t 4") - 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("\nShutting down...") - self.running = False - break - except Exception as e: - print(f"Server error: {e}") - - except Exception as e: - print(f"Failed to start server: {e}") - finally: - print("Server stopped") - -if __name__ == "__main__": - proxy = RinCoinStratumProxyFixed() - proxy.start() diff --git a/rin/proxy/custom/stratum_proxy_original.py b/rin/proxy/custom/stratum_proxy_original.py deleted file mode 100644 index d677f8a..0000000 --- a/rin/proxy/custom/stratum_proxy_original.py +++ /dev/null @@ -1,693 +0,0 @@ -#!/usr/bin/env python3 -""" -RinCoin Stratum Proxy Server - REAL MINING VERSION -Properly constructs Stratum jobs and validates/submits real blocks -""" - -import socket -import threading -import json -import time -import requests -import hashlib -import struct -from requests.auth import HTTPBasicAuth - -class RinCoinStratumBase: - def __init__(self, stratum_host='0.0.0.0', stratum_port=3334, - rpc_host='127.0.0.1', rpc_port=9556, - rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90', - target_address='rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q'): - - 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.target_address = target_address - - self.clients = {} - self.job_counter = 0 - self.current_job = None - self.running = True - self.extranonce1_counter = 0 - - print(f"RinCoin Stratum Proxy Server - REAL MINING") - print(f"Stratum: {stratum_host}:{stratum_port}") - print(f"RPC: {rpc_host}:{rpc_port}") - print(f"Target: {target_address}") - - 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": "stratum_proxy", - "method": method, - "params": params - } - - response = requests.post(url, json=payload, headers=headers, auth=auth, timeout=30) - - 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 encode_varint(self, n): - """Encode integer as Bitcoin-style varint""" - if n < 0xfd: - return bytes([n]) - elif n <= 0xffff: - return b"\xfd" + struct.pack(' bytes: - outputs_blob = b'' - outputs_list = [] - # Main output - outputs_list.append(struct.pack(' 1: - if len(hashes) % 2 == 1: - hashes.append(hashes[-1]) # Duplicate last hash if odd - - next_level = [] - for i in range(0, len(hashes), 2): - combined = hashes[i] + hashes[i + 1] - next_level.append(hashlib.sha256(hashlib.sha256(combined).digest()).digest()) - - hashes = next_level - - return hashes[0] if hashes else b'\x00' * 32 - - except Exception as e: - print(f"Merkle root calculation error: {e}") - return b'\x00' * 32 - - def bits_to_target(self, bits_hex): - """Convert bits to target""" - try: - bits = int(bits_hex, 16) - exponent = bits >> 24 - mantissa = bits & 0xffffff - target = mantissa * (256 ** (exponent - 3)) - return f"{target:064x}" - except: - return "0000ffff00000000000000000000000000000000000000000000000000000000" - - def get_block_template(self): - """Get new block template and create Stratum job""" - try: - template = self.rpc_call("getblocktemplate", [{"rules": ["segwit", "mweb"]}]) - if not template: - return None - - self.job_counter += 1 - - job = { - "job_id": f"job_{self.job_counter:08x}", - "template": template, - "prevhash": template.get("previousblockhash", "0" * 64), - "version": template.get('version', 1), - "bits": template.get('bits', '1d00ffff'), - "ntime": f"{int(time.time()):08x}", - "target": self.bits_to_target(template.get('bits', '1d00ffff')), - "height": template.get('height', 0), - "coinbasevalue": template.get('coinbasevalue', 0), - "transactions": template.get('transactions', []) - } - - self.current_job = job - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - network_difficulty = self.calculate_network_difficulty(job['target']) - print(f"[{timestamp}] 🆕 NEW JOB: {job['job_id']} | Height: {job['height']} | Reward: {job['coinbasevalue']/100000000:.2f} RIN") - print(f" 🎯 Network Difficulty: {network_difficulty:.6f} | Bits: {job['bits']}") - print(f" 📍 Target: {job['target'][:16]}... | Transactions: {len(job['transactions'])}") - return job - - except Exception as e: - print(f"Get block template error: {e}") - return None - - def calculate_share_difficulty(self, hash_hex, target_hex): - """Calculate actual share difficulty from hash""" - try: - hash_int = int(hash_hex, 16) - target_int = int(target_hex, 16) - - if hash_int == 0: - return float('inf') # Perfect hash - - # Bitcoin-style difficulty calculation - # Lower hash = higher difficulty - # Difficulty 1.0 = finding hash that meets network target exactly - max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - - # Share difficulty = how hard this specific hash was to find - difficulty = max_target / hash_int - - return difficulty - except Exception as e: - print(f"Difficulty calculation error: {e}") - return 0.0 - - def calculate_network_difficulty(self, target_hex): - """Calculate network difficulty from target""" - try: - target_int = int(target_hex, 16) - - # Bitcoin difficulty 1.0 target - max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 - - # Network difficulty = how much harder than difficulty 1.0 - network_difficulty = max_target / target_int - - return network_difficulty - except Exception as e: - print(f"Network difficulty calculation error: {e}") - return 1.0 - - def submit_share(self, job, extranonce1, extranonce2, ntime, nonce, target_address=None): - """Validate share and submit block if valid""" - try: - # Use provided address or default - address = target_address or self.target_address - - # Build coinbase (with and without witness) - coinbase_wit, coinbase_nowit = self.build_coinbase_transaction_for_address( - job['template'], extranonce1, extranonce2, address) - if not coinbase_wit or not coinbase_nowit: - return False, "Coinbase construction failed" - - # Calculate coinbase txid (non-witness serialization) - coinbase_txid = hashlib.sha256(hashlib.sha256(coinbase_nowit).digest()).digest()[::-1] - - # Calculate merkle root - merkle_root = self.calculate_merkle_root(coinbase_txid, job['transactions']) - - # Build block header - header = b'' - header += struct.pack(' 0 else 0 - - # Progress indicator based on percentage - if difficulty_percentage >= 100: - progress_icon = "🎉" # Block found! - elif difficulty_percentage >= 50: - progress_icon = "🔥" # Very close - elif difficulty_percentage >= 10: - progress_icon = "⚡" # Getting warm - elif difficulty_percentage >= 1: - progress_icon = "💫" # Some progress - else: - progress_icon = "📊" # Low progress - - print(f"[{timestamp}] {progress_icon} SHARE: job={job['job_id']} | nonce={nonce} | hash={block_hash_hex[:16]}...") - print(f" 🎯 Share Diff: {share_difficulty:.2e} | Network Diff: {network_difficulty:.6f}") - print(f" 📈 Progress: {difficulty_percentage:.4f}% of network difficulty") - print(f" 📍 Target: {job['target'][:16]}... | Height: {job['height']}") - print(f" ⏰ Time: {ntime} | Extranonce: {extranonce1}:{extranonce2}") - - if hash_int > target_int: - # Valid share but not a block - still send to node for validation - print(f" ✅ Share accepted (below network difficulty)") - - # Send to node anyway to validate our work - try: - # Build complete block for validation - block = header - tx_count = 1 + len(job['transactions']) - block += self.encode_varint(tx_count) - block += coinbase_wit - for tx in job['transactions']: - block += bytes.fromhex(tx['data']) - - block_hex = block.hex() - print(f" 🔍 Sending share to node for validation...") - result = self.rpc_call("submitblock", [block_hex]) - - if result is None: - print(f" 🎉 SURPRISE BLOCK! Node accepted our 'low difficulty' share as valid block!") - return True, "Block found and submitted" - else: - print(f" 📊 Node rejected as expected: {result}") - return True, "Share validated by node" - - except Exception as e: - print(f" ⚠️ Node validation error: {e}") - return True, "Share accepted (node validation failed)" - - return True, "Share accepted" - - # Valid block! Build full block and submit - print(f" 🎉 BLOCK FOUND! Hash: {block_hash_hex}") - print(f" 💰 Reward: {job['coinbasevalue']/100000000:.2f} RIN -> {address}") - print(f" 📊 Block height: {job['height']}") - print(f" 🔍 Difficulty: {share_difficulty:.6f} (target: {network_difficulty:.6f})") - - # Build complete block - block = header - - # Transaction count - tx_count = 1 + len(job['transactions']) - block += self.encode_varint(tx_count) - - # Add coinbase transaction (witness variant for block body) - block += coinbase_wit - - # Add other transactions - for tx in job['transactions']: - block += bytes.fromhex(tx['data']) - - # Submit block - block_hex = block.hex() - print(f" 📦 Submitting block of size {len(block_hex)//2} bytes...") - - result = self.rpc_call("submitblock", [block_hex]) - - if result is None: - print(f" ✅ Block accepted by network!") - return True, "Block found and submitted" - else: - print(f" ❌ Block rejected: {result}") - print(f" 🔍 Debug: Block size {len(block_hex)//2} bytes, {len(job['transactions'])} transactions") - return False, f"Block rejected: {result}" - - except Exception as e: - print(f"Share submission error: {e}") - return False, f"Submission error: {e}" - - except Exception as e: - print(f"Share submission error: {e}") - return False, f"Submission error: {e}" - - 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", []) - - if method == "mining.subscribe": - # Generate unique extranonce1 for this connection - self.extranonce1_counter += 1 - extranonce1 = f"{self.extranonce1_counter:08x}" - - # Store extranonce1 for this client - if addr not in self.clients: - self.clients[addr] = {} - self.clients[addr]['extranonce1'] = extranonce1 - - # Subscribe response - self.send_stratum_response(client, msg_id, [ - [["mining.set_difficulty", "subscription_id"], ["mining.notify", "subscription_id"]], - extranonce1, - 4 # extranonce2 size - ]) - - # Send difficulty - self.send_stratum_notification(client, "mining.set_difficulty", [0.0001]) - - # Send initial job - if self.current_job: - self.send_job_to_client(client, self.current_job) - else: - if self.get_block_template(): - self.send_job_to_client(client, self.current_job) - - elif method == "mining.authorize": - username = params[0] if params else "anonymous" - self.clients[addr]['username'] = username - self.send_stratum_response(client, msg_id, True) - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - print(f"[{timestamp}] 🔐 [{addr}] Authorized as {username}") - - elif method == "mining.extranonce.subscribe": - # Handle extranonce subscription - self.send_stratum_response(client, msg_id, True) - - elif method == "mining.submit": - if len(params) >= 5: - username = params[0] - job_id = params[1] - extranonce2 = params[2] - ntime = params[3] - nonce = params[4] - - print(f"[{addr}] Submit: {username} | job={job_id} | nonce={nonce}") - - # Validate submission - if self.current_job and job_id == self.current_job['job_id']: - extranonce1 = self.clients[addr].get('extranonce1', '00000000') - - # Submit share - success, message = self.submit_share(self.current_job, extranonce1, extranonce2, ntime, nonce) - - if success: - self.send_stratum_response(client, msg_id, True) - if "Block found" in message: - # Get new job after block found - threading.Thread(target=self.update_job_after_block, daemon=True).start() - else: - self.send_stratum_response(client, msg_id, False, message) - else: - # For stale jobs, still validate for blocks but don't require exact job match - # This prevents missing blocks due to job timing issues - if self.current_job: - extranonce1 = self.clients[addr].get('extranonce1', '00000000') - # Use current job template but allow stale job_id - success, message = self.submit_share(self.current_job, extranonce1, extranonce2, ntime, nonce) - - if success: - self.send_stratum_response(client, msg_id, True) - if "Block found" in message: - # Get new job after block found - threading.Thread(target=self.update_job_after_block, daemon=True).start() - else: - # Accept as share even if block validation fails for stale jobs - self.send_stratum_response(client, msg_id, True) - else: - self.send_stratum_response(client, msg_id, True) - else: - self.send_stratum_response(client, msg_id, False, "Invalid parameters") - - else: - print(f"[{addr}] Unknown method: {method}") - self.send_stratum_response(client, msg_id, None, "Unknown method") - - except json.JSONDecodeError: - print(f"[{addr}] Invalid JSON: {message}") - except Exception as e: - print(f"[{addr}] Message handling error: {e}") - - def send_job_to_client(self, client, job): - """Send mining job to specific client""" - try: - # Send proper Stratum job - self.send_stratum_notification(client, "mining.notify", [ - job["job_id"], - job["prevhash"], - "", # coinb1 (empty for now - miner handles coinbase) - "", # coinb2 (empty for now - miner handles coinbase) - [], # merkle_branch (empty for now - we calculate merkle root) - f"{job['version']:08x}", - job["bits"], - job["ntime"], - True # clean_jobs - ]) - except Exception as e: - print(f"Failed to send job: {e}") - - def update_job_after_block(self): - """Update job after a block is found""" - time.sleep(2) # Brief delay to let network propagate - if self.get_block_template(): - self.broadcast_new_job() - - def broadcast_new_job(self): - """Broadcast new job to all connected clients""" - if not self.current_job: - return - - print(f"Broadcasting new job to {len(self.clients)} clients") - - for addr, client_data in list(self.clients.items()): - try: - if 'socket' in client_data: - self.send_job_to_client(client_data['socket'], self.current_job) - except Exception as e: - print(f"Failed to send job to {addr}: {e}") - - def handle_client(self, client, addr): - """Handle individual client connection""" - print(f"[{addr}] Connected") - if addr not in self.clients: - self.clients[addr] = {} - self.clients[addr]['socket'] = client - - 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""" - while self.running: - try: - time.sleep(30) # Update every 30 seconds - - old_height = self.current_job['height'] if self.current_job else 0 - - if self.get_block_template(): - new_height = self.current_job['height'] - if new_height > old_height: - print(f"New block detected! Broadcasting new job...") - self.broadcast_new_job() - - except Exception as e: - print(f"Job updater error: {e}") - - def start(self): - """Start the Stratum proxy 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") - print(f"Current height: {blockchain_info.get('blocks', 'unknown')}") - print(f"Chain: {blockchain_info.get('chain', 'unknown')}") - - # Get initial block template - if not self.get_block_template(): - print("Failed to get initial block template!") - return - - # Start job updater thread - job_thread = threading.Thread(target=self.job_updater, daemon=True) - job_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) - - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - print(f"[{timestamp}] 🚀 REAL Mining Stratum proxy ready!") - print(f" 📡 Listening on {self.stratum_host}:{self.stratum_port}") - print(f" 💰 Mining to: {self.target_address}") - print(f" 📊 Current job: {self.current_job['job_id'] if self.current_job else 'None'}") - print("") - print(" 🔧 Miner command:") - print(f" ./cpuminer -a rinhash -o stratum+tcp://{self.stratum_host}:{self.stratum_port} -u worker1 -p x -t 4") - 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("\nShutting down...") - self.running = False - break - except Exception as e: - print(f"Server error: {e}") - - except Exception as e: - print(f"Failed to start server: {e}") - finally: - print("Server stopped") - -class RinCoinStratumProxy(RinCoinStratumBase): - """Solo mining stratum proxy implementation""" - - def __init__(self, stratum_host='0.0.0.0', stratum_port=3334, - rpc_host='127.0.0.1', rpc_port=9556, - rpc_user='rinrpc', rpc_password='745ce784d5d537fc06105a1b935b7657903cfc71a5fb3b90', - target_address='rin1qahvvv9d5f3443wtckeqavwp9950wacxfmwv20q'): - super().__init__(stratum_host, stratum_port, rpc_host, rpc_port, rpc_user, rpc_password, target_address) - -if __name__ == "__main__": - proxy = RinCoinStratumProxy() - proxy.start() \ No newline at end of file