stratum wip - valid block info
This commit is contained in:
@@ -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
|
||||
|
Reference in New Issue
Block a user