From 2bcd28be2872e10dc7b45581ef93dcc4fc9ce214 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 17 Sep 2025 17:53:40 +0300 Subject: [PATCH] proxy fix --- MINE/rin/start_stratum_proxy.sh | 1 - MINE/rin/stratum_proxy.py | 222 +++++----- MINE/rin/stratum_proxy_debug.py | 705 ++++++++++++++++++++++++++++++++ MINE/rin/view_mining_log.sh | 45 ++ 4 files changed, 867 insertions(+), 106 deletions(-) create mode 100644 MINE/rin/stratum_proxy_debug.py create mode 100644 MINE/rin/view_mining_log.sh diff --git a/MINE/rin/start_stratum_proxy.sh b/MINE/rin/start_stratum_proxy.sh index f6ad91f..4bf852c 100644 --- a/MINE/rin/start_stratum_proxy.sh +++ b/MINE/rin/start_stratum_proxy.sh @@ -56,7 +56,6 @@ fi echo "" echo "🚀 Starting Stratum Proxy Server..." -echo "This will bridge cpuminer-opt-rin to your RinCoin node" 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\"" diff --git a/MINE/rin/stratum_proxy.py b/MINE/rin/stratum_proxy.py index d677f8a..e9e4bd6 100644 --- a/MINE/rin/stratum_proxy.py +++ b/MINE/rin/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,11 +33,57 @@ class RinCoinStratumBase: self.running = True self.extranonce1_counter = 0 - print(f"RinCoin Stratum Proxy Server - REAL MINING") + # Logging setup + self.log_file = "mining_log.txt" + self.init_log_file() + + 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}") + def init_log_file(self): + """Initialize mining log file with header""" + try: + with open(self.log_file, 'w') as f: + f.write("=" * 80 + "\n") + f.write("RinCoin Mining Log\n") + f.write("=" * 80 + "\n") + f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Target Address: {self.target_address}\n") + f.write(f"Stratum: {self.stratum_host}:{self.stratum_port}\n") + f.write(f"RPC: {self.rpc_host}:{self.rpc_port}\n") + f.write("=" * 80 + "\n\n") + except Exception as e: + print(f"Failed to initialize log file: {e}") + + def log_hash_found(self, hash_hex, difficulty, height, reward, nonce, ntime): + """Log found hash to file""" + try: + timestamp = time.strftime('%Y-%m-%d %H:%M:%S') + with open(self.log_file, 'a') as f: + f.write(f"[{timestamp}] 🎉 HASH FOUND!\n") + f.write(f" Hash: {hash_hex}\n") + f.write(f" Difficulty: {difficulty:.6f}\n") + f.write(f" Height: {height}\n") + f.write(f" Reward: {reward:.8f} RIN\n") + f.write(f" Nonce: {nonce}\n") + f.write(f" Time: {ntime}\n") + f.write("-" * 40 + "\n") + except Exception as e: + print(f"Failed to log hash: {e}") + + def log_wallet_balance(self): + """Log current wallet balance to file""" + try: + balance = self.rpc_call("getbalance") + if balance is not None: + timestamp = time.strftime('%Y-%m-%d %H:%M:%S') + with open(self.log_file, 'a') as f: + f.write(f"[{timestamp}] 💰 Wallet Balance: {balance:.8f} RIN\n") + except Exception as e: + print(f"Failed to log wallet balance: {e}") + def rpc_call(self, method, params=[]): """Make RPC call to RinCoin node""" try: @@ -196,14 +242,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 +294,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 +314,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 +330,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 +347,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 +390,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}") @@ -376,6 +403,10 @@ class RinCoinStratumBase: print(f" 📊 Block height: {job['height']}") print(f" 🔍 Difficulty: {share_difficulty:.6f} (target: {network_difficulty:.6f})") + # Log the found hash + reward_rin = job['coinbasevalue'] / 100000000 + self.log_hash_found(block_hash_hex, share_difficulty, job['height'], reward_rin, nonce, ntime) + # Build complete block block = header @@ -398,6 +429,8 @@ class RinCoinStratumBase: if result is None: print(f" ✅ Block accepted by network!") + # Log wallet balance after successful block submission + self.log_wallet_balance() return True, "Block found and submitted" else: print(f" ❌ Block rejected: {result}") @@ -407,10 +440,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 +492,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 +510,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 +522,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 +552,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"], @@ -605,6 +615,7 @@ class RinCoinStratumBase: def job_updater(self): """Periodically update mining jobs""" + balance_log_counter = 0 while self.running: try: time.sleep(30) # Update every 30 seconds @@ -616,6 +627,12 @@ class RinCoinStratumBase: if new_height > old_height: print(f"New block detected! Broadcasting new job...") self.broadcast_new_job() + + # Log wallet balance every 10 minutes (20 cycles of 30 seconds) + balance_log_counter += 1 + if balance_log_counter >= 20: + self.log_wallet_balance() + balance_log_counter = 0 except Exception as e: print(f"Job updater error: {e}") @@ -633,6 +650,9 @@ class RinCoinStratumBase: print(f"Current height: {blockchain_info.get('blocks', 'unknown')}") print(f"Chain: {blockchain_info.get('chain', 'unknown')}") + # Log initial wallet balance + self.log_wallet_balance() + # Get initial block template if not self.get_block_template(): print("Failed to get initial block template!") @@ -649,10 +669,11 @@ 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'}") + print(f" 📝 Mining log: {self.log_file}") print("") print(" 🔧 Miner command:") print(f" ./cpuminer -a rinhash -o stratum+tcp://{self.stratum_host}:{self.stratum_port} -u worker1 -p x -t 4") @@ -679,15 +700,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/MINE/rin/stratum_proxy_debug.py b/MINE/rin/stratum_proxy_debug.py new file mode 100644 index 0000000..e9e4bd6 --- /dev/null +++ b/MINE/rin/stratum_proxy_debug.py @@ -0,0 +1,705 @@ +#!/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 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', + 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 + + # Logging setup + self.log_file = "mining_log.txt" + self.init_log_file() + + 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}") + + def init_log_file(self): + """Initialize mining log file with header""" + try: + with open(self.log_file, 'w') as f: + f.write("=" * 80 + "\n") + f.write("RinCoin Mining Log\n") + f.write("=" * 80 + "\n") + f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Target Address: {self.target_address}\n") + f.write(f"Stratum: {self.stratum_host}:{self.stratum_port}\n") + f.write(f"RPC: {self.rpc_host}:{self.rpc_port}\n") + f.write("=" * 80 + "\n\n") + except Exception as e: + print(f"Failed to initialize log file: {e}") + + def log_hash_found(self, hash_hex, difficulty, height, reward, nonce, ntime): + """Log found hash to file""" + try: + timestamp = time.strftime('%Y-%m-%d %H:%M:%S') + with open(self.log_file, 'a') as f: + f.write(f"[{timestamp}] 🎉 HASH FOUND!\n") + f.write(f" Hash: {hash_hex}\n") + f.write(f" Difficulty: {difficulty:.6f}\n") + f.write(f" Height: {height}\n") + f.write(f" Reward: {reward:.8f} RIN\n") + f.write(f" Nonce: {nonce}\n") + f.write(f" Time: {ntime}\n") + f.write("-" * 40 + "\n") + except Exception as e: + print(f"Failed to log hash: {e}") + + def log_wallet_balance(self): + """Log current wallet balance to file""" + try: + balance = self.rpc_call("getbalance") + if balance is not None: + timestamp = time.strftime('%Y-%m-%d %H:%M:%S') + with open(self.log_file, 'a') as f: + f.write(f"[{timestamp}] 💰 Wallet Balance: {balance:.8f} RIN\n") + except Exception as e: + print(f"Failed to log wallet balance: {e}") + + 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})") + + # Log the found hash + reward_rin = job['coinbasevalue'] / 100000000 + self.log_hash_found(block_hash_hex, share_difficulty, job['height'], reward_rin, nonce, ntime) + + # 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!") + # Log wallet balance after successful block submission + self.log_wallet_balance() + 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""" + balance_log_counter = 0 + 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() + + # Log wallet balance every 10 minutes (20 cycles of 30 seconds) + balance_log_counter += 1 + if balance_log_counter >= 20: + self.log_wallet_balance() + balance_log_counter = 0 + + 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')}") + + # Log initial wallet balance + self.log_wallet_balance() + + # 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}] 🚀 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(f" 📝 Mining log: {self.log_file}") + 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 = RinCoinStratumProxy() + proxy.start() diff --git a/MINE/rin/view_mining_log.sh b/MINE/rin/view_mining_log.sh new file mode 100644 index 0000000..de4ec60 --- /dev/null +++ b/MINE/rin/view_mining_log.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# View RinCoin Mining Log +# Shows the latest mining activity and wallet balance + +LOG_FILE="mining_log.txt" + +if [ ! -f "$LOG_FILE" ]; then + echo "❌ Mining log file '$LOG_FILE' not found!" + echo "Make sure the stratum proxy has been started at least once." + exit 1 +fi + +echo "=== RinCoin Mining Log Viewer ===" +echo "" + +# Show last 20 lines of the log +echo "📊 Recent Activity (last 20 entries):" +echo "----------------------------------------" +tail -20 "$LOG_FILE" + +echo "" +echo "💰 Current Wallet Balance:" +echo "----------------------------------------" +grep "Wallet Balance:" "$LOG_FILE" | tail -1 || echo "No balance logged yet" + +echo "" +echo "🎉 Total Blocks Found:" +echo "----------------------------------------" +grep -c "HASH FOUND!" "$LOG_FILE" || echo "0" + +echo "" +echo "📈 Total Mining Time:" +echo "----------------------------------------" +if grep -q "Started:" "$LOG_FILE"; then + START_TIME=$(grep "Started:" "$LOG_FILE" | head -1 | cut -d' ' -f2-3) + echo "Started: $START_TIME" + echo "Current: $(date '+%Y-%m-%d %H:%M:%S')" +else + echo "Start time not available" +fi + +echo "" +echo "📝 Full log available at: $LOG_FILE" +echo "🔄 To watch live updates: tail -f $LOG_FILE"