pool uses proxy

This commit is contained in:
Dobromir Popov
2025-09-05 00:57:22 +03:00
parent fec5f35cce
commit 48060c360f
3 changed files with 183 additions and 282 deletions

View File

@@ -13,7 +13,7 @@ import hashlib
import struct
from requests.auth import HTTPBasicAuth
class RinCoinStratumProxy:
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',
@@ -82,93 +82,92 @@ class RinCoinStratumProxy:
def decode_bech32_address(self, address):
"""Decode RinCoin bech32 address to script"""
try:
if not address.startswith('rin1'):
if not address or not address.startswith('rin1'):
raise ValueError("Not a RinCoin bech32 address")
# Use generatetoaddress to get a proper script for this address
# This ensures we create valid outputs that can actually be spent
result = self.rpc_call("validateaddress", [address])
if result and result.get('isvalid') and 'scriptPubKey' in result:
script_hex = result['scriptPubKey']
return bytes.fromhex(script_hex)
else:
# Fallback: create a basic P2WPKH script if RPC validation fails
# Extract the witness program (assuming it's standard 20-byte)
# This is a simplified approach - in production use a proper bech32 library
print(f"Warning: Using fallback script for {address}")
return bytes([0x00, 0x14]) + bytes(20) # OP_0 + 20 zero bytes
if not result or not result.get('isvalid'):
raise ValueError("Address not valid per node")
script_hex = result.get('scriptPubKey')
if not script_hex:
raise ValueError("Node did not return scriptPubKey")
return bytes.fromhex(script_hex)
except Exception as e:
print(f"Address decode error: {e}")
# Emergency fallback - this should never happen in production
return bytes([0x00, 0x14]) + bytes(20)
return None
def build_coinbase_transaction(self, template, extranonce1, extranonce2):
"""Build complete coinbase transaction"""
"""Build coinbase transaction variants (with and without witness) for default address"""
return self.build_coinbase_transaction_for_address(template, extranonce1, extranonce2, self.target_address)
def build_coinbase_transaction_for_address(self, template, extranonce1, extranonce2, target_address):
"""Build coinbase transaction variants (with and without witness)"""
try:
coinbase = b''
# Version
coinbase += struct.pack('<I', 1)
# Check if we need segwit format based on template
has_witness_commitment = template.get('default_witness_commitment') is not None
if has_witness_commitment:
# Segwit marker and flag (only if witness commitment present)
coinbase += b'\x00\x01'
# Input count
coinbase += b'\x01'
# Coinbase input
coinbase += b'\x00' * 32 # prev hash (null)
coinbase += b'\xff\xff\xff\xff' # prev index
# Script sig (height + extranonces + signature)
# Common parts
value = template.get('coinbasevalue', 0)
script_pubkey = self.decode_bech32_address(target_address)
if not script_pubkey:
return None, None
witness_commitment = template.get('default_witness_commitment')
# ScriptSig (block height minimal push + tag + extranonces)
height = template.get('height', 0)
height_bytes = struct.pack('<I', height)
height_compact = bytes([len(height_bytes.rstrip(b'\x00'))]) + height_bytes.rstrip(b'\x00')
scriptsig = height_compact + b'/RinCoin/' + extranonce1.encode() + extranonce2.encode()
coinbase += self.encode_varint(len(scriptsig)) + scriptsig
# Sequence
coinbase += b'\xff\xff\xff\xff'
# Prepare outputs
outputs = []
# Main output to mining address
value = template.get('coinbasevalue', 0)
script_pubkey = self.decode_bech32_address(self.target_address)
outputs.append(struct.pack('<Q', value) + self.encode_varint(len(script_pubkey)) + script_pubkey)
# Witness commitment output if present
witness_commitment = template.get('default_witness_commitment')
if witness_commitment:
commit_script = bytes.fromhex(witness_commitment)
outputs.append(struct.pack('<Q', 0) + self.encode_varint(len(commit_script)) + commit_script)
# Add outputs
coinbase += self.encode_varint(len(outputs))
for output in outputs:
coinbase += output
# Witness data (only if segwit format)
# Helper to build outputs blob
def build_outputs_blob() -> bytes:
outputs_blob = b''
outputs_list = []
# Main output
outputs_list.append(struct.pack('<Q', value) + self.encode_varint(len(script_pubkey)) + script_pubkey)
# Witness commitment OP_RETURN output if present
if witness_commitment:
commit_script = bytes.fromhex(witness_commitment)
outputs_list.append(struct.pack('<Q', 0) + self.encode_varint(len(commit_script)) + commit_script)
outputs_blob += self.encode_varint(len(outputs_list))
for out in outputs_list:
outputs_blob += out
return outputs_blob
# Build non-witness serialization (txid serialization)
cb_nowit = b''
cb_nowit += struct.pack('<I', 1) # version
cb_nowit += b'\x01' # input count
cb_nowit += b'\x00' * 32 # prevout hash
cb_nowit += b'\xff\xff\xff\xff' # prevout index
cb_nowit += self.encode_varint(len(scriptsig)) + scriptsig
cb_nowit += b'\xff\xff\xff\xff' # sequence
cb_nowit += build_outputs_blob() # outputs
cb_nowit += struct.pack('<I', 0) # locktime
# Build with-witness serialization (block serialization)
if has_witness_commitment:
coinbase += b'\x01' # witness stack count for input
coinbase += b'\x20' # witness item length (32 bytes)
coinbase += b'\x00' * 32 # witness nonce
# Locktime
coinbase += struct.pack('<I', 0)
return coinbase
cb_wit = b''
cb_wit += struct.pack('<I', 1) # version
cb_wit += b'\x00\x01' # segwit marker+flag
cb_wit += b'\x01' # input count
cb_wit += b'\x00' * 32 # prevout hash
cb_wit += b'\xff\xff\xff\xff' # prevout index
cb_wit += self.encode_varint(len(scriptsig)) + scriptsig
cb_wit += b'\xff\xff\xff\xff' # sequence
cb_wit += build_outputs_blob() # outputs
# Witness stack for coinbase (32-byte reserved value)
cb_wit += b'\x01' # witness stack count
cb_wit += b'\x20' # item length
cb_wit += b'\x00' * 32 # reserved value
cb_wit += struct.pack('<I', 0) # locktime
else:
cb_wit = cb_nowit
return cb_wit, cb_nowit
except Exception as e:
print(f"Coinbase construction error: {e}")
return None
return None, None
def calculate_merkle_root(self, coinbase_txid, transactions):
"""Calculate merkle root with coinbase at index 0"""
@@ -237,21 +236,25 @@ class RinCoinStratumProxy:
print(f"Get block template error: {e}")
return None
def submit_share(self, job, extranonce1, extranonce2, ntime, nonce):
def submit_share(self, job, extranonce1, extranonce2, ntime, nonce, target_address=None):
"""Validate share and submit block if valid"""
try:
print(f"Share: job={job['job_id']} nonce={nonce}")
# Build coinbase transaction
coinbase = self.build_coinbase_transaction(job['template'], extranonce1, extranonce2)
if not coinbase:
# 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 transaction hash
coinbase_hash = hashlib.sha256(hashlib.sha256(coinbase).digest()).digest()[::-1]
# 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_hash, job['transactions'])
merkle_root = self.calculate_merkle_root(coinbase_txid, job['transactions'])
# Build block header
header = b''
@@ -284,8 +287,8 @@ class RinCoinStratumProxy:
tx_count = 1 + len(job['transactions'])
block += self.encode_varint(tx_count)
# Add coinbase transaction
block += coinbase
# Add coinbase transaction (witness variant for block body)
block += coinbase_wit
# Add other transactions
for tx in job['transactions']:
@@ -299,7 +302,7 @@ class RinCoinStratumProxy:
if result is None:
print(f"✅ Block accepted: {block_hash_hex}")
print(f"💰 Reward: {job['coinbasevalue']/100000000:.2f} RIN -> {self.target_address}")
print(f"💰 Reward: {job['coinbasevalue']/100000000:.2f} RIN -> {address}")
print(f"📊 Block height: {job['height']}")
return True, "Block found and submitted"
else:
@@ -578,6 +581,15 @@ class RinCoinStratumProxy:
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()