stratum wip - valid block info

This commit is contained in:
Dobromir Popov
2025-09-23 11:39:03 +03:00
parent bc50e52cdb
commit d8b019401f

View File

@@ -613,28 +613,97 @@ class RinCoinStratumProxy:
print(f"[{addr}] Message handling error: {e}")
def send_job_to_client(self, client, job):
"""Send mining job to specific client"""
"""Send mining job to specific client with proper stratum parameters"""
try:
# Build coinbase components for stratum
# Get network difficulty for display
network_difficulty = self.calculate_network_difficulty(job['target'])
# Build proper coinbase transaction parts for stratum protocol
template = job.get('template', {})
height = job.get('height', 0)
height_bytes = struct.pack('<I', height)
height_compact = bytes([len(height_bytes.rstrip(b'\x00'))]) + height_bytes.rstrip(b'\x00')
# coinb1: prefix up to extranonce1 position
coinb1 = height_compact + b'/RinCoin/'
# Encode height as minimal push (BIP34 compliance)
if height == 0:
height_push = b''
else:
height_bytes = struct.pack('<I', height)
# Remove trailing zero bytes
while len(height_bytes) > 1 and height_bytes[-1] == 0:
height_bytes = height_bytes[:-1]
height_push = bytes([len(height_bytes)]) + height_bytes
# coinb2: empty (extranonce2 goes at the end)
# Build coinbase input script: height + arbitrary data + extranonces
coinb1_script = height_push + b'/RinCoin/'
# Build coinbase transaction structure
# Version (4 bytes)
coinb1 = struct.pack('<I', 1)
# Input count (1 byte)
coinb1 += b'\x01'
# Previous output hash (32 bytes of zeros)
coinb1 += b'\x00' * 32
# Previous output index (4 bytes of 0xFF)
coinb1 += b'\xff\xff\xff\xff'
# Script length (will include extranonces)
script_len_pos = len(coinb1)
coinb1 += b'\x00' # Placeholder for script length
# Script data up to extranonce1
coinb1 += coinb1_script
# coinb2: everything after extranonces
# For now, just the sequence and outputs
coinb2 = b''
coinb2 += b'\xff\xff\xff\xff' # Sequence
# Calculate merkle branches for transactions
# Start with coinbase txid placeholder and all transaction hashes
coinbase_txid_placeholder = b'\x00' * 32 # Placeholder, miner will calculate
tx_hashes = [coinbase_txid_placeholder]
# Outputs
script_pubkey = self.decode_bech32_address(self.target_address)
if script_pubkey:
value = template.get('coinbasevalue', 0)
# Output count
output_count = 1
witness_commitment = template.get('default_witness_commitment')
if witness_commitment:
output_count = 2
coinb2 += self.encode_varint(output_count)
# Main output (to mining address)
coinb2 += struct.pack('<Q', value)
coinb2 += self.encode_varint(len(script_pubkey))
coinb2 += script_pubkey
# Witness commitment output if present
if witness_commitment:
commit_script = bytes.fromhex(witness_commitment)
coinb2 += struct.pack('<Q', 0) # Zero value
coinb2 += self.encode_varint(len(commit_script))
coinb2 += commit_script
# Locktime
coinb2 += struct.pack('<I', 0)
# Calculate total script length (coinb1_script + extranonces lengths)
# extranonce1 is 8 hex chars (4 bytes), extranonce2 is 8 hex chars (4 bytes)
total_script_len = len(coinb1_script) + 4 + 4 # extranonce1 + extranonce2 (binary)
# Update script length in coinb1
coinb1_list = list(coinb1)
if total_script_len < 253:
coinb1_list[script_len_pos] = total_script_len
else:
# Handle larger script lengths with varint encoding
coinb1 = coinb1[:script_len_pos] + self.encode_varint(total_script_len) + coinb1[script_len_pos+1:]
coinb1 = bytes(coinb1_list) if total_script_len < 253 else coinb1
# Calculate merkle branches
tx_hashes = []
for tx in job.get('transactions', []):
tx_hashes.append(bytes.fromhex(tx['hash'])[::-1]) # Little-endian
# Build merkle tree and get branches
merkle_branches = self.calculate_merkle_branches(tx_hashes, 0) # For coinbase at index 0
merkle_branches = []
if tx_hashes:
# For coinbase at index 0, calculate merkle path
merkle_branches = self.calculate_merkle_branches([b'\x00' * 32] + tx_hashes, 0)
self.send_stratum_notification(client, "mining.notify", [
job["job_id"],
@@ -647,8 +716,13 @@ class RinCoinStratumProxy:
job["ntime"],
True # clean_jobs
])
print(f" 📤 Sent job to miner: Height {height} | NetDiff {network_difficulty:.6f} | Txs {len(job.get('transactions', []))}")
except Exception as e:
print(f"Failed to send job: {e}")
import traceback
traceback.print_exc()
def update_job_after_block(self):
"""Update job after a block is found"""
@@ -721,25 +795,106 @@ class RinCoinStratumProxy:
except Exception as e:
print(f"Job updater error: {e}")
def validate_mining_capability(self):
"""Validate that we can mine valid blocks against the RinCoin node"""
try:
print("🔍 Validating mining capability...")
# Get block template
template = self.rpc_call("getblocktemplate", [{"rules": ["segwit", "mweb"]}])
if not template:
print("❌ Cannot get block template")
return False
# Test address validation
result = self.rpc_call("validateaddress", [self.target_address])
if not result or not result.get('isvalid'):
print(f"❌ Target address {self.target_address} is invalid")
return False
print(f"✅ Target address {self.target_address} is valid")
# Test coinbase construction
coinbase_wit, coinbase_nowit = self.build_coinbase_transaction_for_address(
template, "00000001", "01000000", self.target_address)
if not coinbase_wit or not coinbase_nowit:
print("❌ Cannot construct coinbase transaction")
return False
print(f"✅ Coinbase construction works")
# Test block header construction with dummy values
test_nonce = "12345678"
test_ntime = f"{int(time.time()):08x}"
# Calculate merkle root
coinbase_txid = hashlib.sha256(hashlib.sha256(coinbase_nowit).digest()).digest()[::-1]
merkle_root = self.calculate_merkle_root(coinbase_txid, template.get('transactions', []))
# Build test header
header = b''
header += struct.pack('<I', template.get('version', 1))
header += bytes.fromhex(template.get("previousblockhash", "0" * 64))[::-1]
header += merkle_root
header += struct.pack('<I', int(test_ntime, 16))
header += bytes.fromhex(template.get('bits', '1d00ffff'))
header += struct.pack('<I', int(test_nonce, 16))
# Calculate hash
block_hash = hashlib.sha256(hashlib.sha256(header).digest()).digest()
block_hash_hex = block_hash[::-1].hex()
print(f"✅ Block header construction works")
print(f" Test hash: {block_hash_hex}")
# Check difficulty calculation
target = self.bits_to_target(template.get('bits', '1d00ffff'))
network_diff = self.calculate_network_difficulty(target)
print(f"✅ Difficulty calculation works")
print(f" Network difficulty: {network_diff:.6f}")
print(f" Bits: {template.get('bits', '1d00ffff')}")
print(f" Target: {target[:16]}...")
# Test RPC submission (dry run - don't actually submit)
print(f"✅ Ready to mine valid blocks!")
print(f" Block height: {template.get('height', 0)}")
print(f" Reward: {template.get('coinbasevalue', 0) / 100000000:.2f} RIN")
print(f" Transactions: {len(template.get('transactions', []))}")
return True
except Exception as e:
print(f"❌ Mining validation failed: {e}")
import traceback
traceback.print_exc()
return False
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!")
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')}")
print(f"Connected to RinCoin node (block {blockchain_info.get('blocks', 'unknown')})")
print(f" Chain: {blockchain_info.get('chain', 'unknown')}")
print(f" Difficulty: {blockchain_info.get('difficulty', 'unknown')}")
# Validate mining capability
if not self.validate_mining_capability():
print("❌ Mining validation failed - cannot continue")
return
# 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!")
print("Failed to get initial block template!")
return
# Start job updater thread