pool uses proxy
This commit is contained in:
@@ -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()
|
Reference in New Issue
Block a user