""" Integrates the mining system with Bitcoin mainnet """ from typing import Dict, Any, Optional import time import requests import hashlib import json import logging import struct # Configure detailed logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('network_debug.log'), logging.StreamHandler() ] ) class NetworkIntegration: def __init__(self, wallet_address: str = None): self.api_base = "https://api.bitaps.com/btc/v1" self.node = "seed.bitcoin.sipa.be" # Bitcoin mainnet seed node # Use the provided wallet address or load from my_wallet.json if wallet_address: self.wallet_address = wallet_address else: try: with open('my_wallet.json', 'r') as f: wallet_data = json.load(f) self.wallet_address = wallet_data['address'] print(f"Using wallet address: {self.wallet_address}") except Exception as e: print(f"Error loading wallet: {e}") self.wallet_address = "1Ks4WtCEK96BaBF7HSuCGt3rEpVKPqcJKf" # Your default address def connect(self) -> bool: """Connect to Bitcoin mainnet""" try: # Test connection by getting latest block response = requests.get(f"{self.api_base}/blockchain/blocks/last") return response.status_code == 200 except Exception as e: print(f"Failed to connect to mainnet: {e}") return False def get_block_template(self) -> Dict[str, Any]: """Get current block template from mainnet""" try: # Cache the blockchain API response for 5 minutes current_time = time.time() if not hasattr(self, '_template_cache') or current_time - self._last_cache_time > 300: logging.debug("Cache expired, fetching new block template") # Get latest block info from a more reliable API response = requests.get("https://blockchain.info/latestblock") logging.debug(f"Latest block API response status: {response.status_code}") if response.status_code != 200: logging.error(f"Failed to get latest block. Status code: {response.status_code}") if hasattr(self, '_template_cache'): logging.info("Using cached template") return self._template_cache raise Exception("Failed to get latest block") latest = response.json() logging.debug(f"Latest block response: {latest}") height = latest['height'] current_block = latest['hash'] logging.info(f"Current block height: {height}, hash: {current_block}") # Get current network stats logging.debug("Fetching network stats...") stats_response = requests.get("https://blockchain.info/stats?format=json") logging.debug(f"Network stats API response status: {stats_response.status_code}") stats = stats_response.json() # Convert difficulty to target difficulty = float(stats.get('difficulty', 1)) max_target = int('00000000FFFF0000000000000000000000000000000000000000000000000000', 16) current_target = int(max_target / difficulty) bits = stats.get('bits', '1d00ffff') if isinstance(bits, str): bits = int(bits, 16) # Construct template with required fields template = { 'version': 2, # Current Bitcoin version 'previousblockhash': current_block, # Use current block as previous for next block 'merkleroot': '0' * 64, # Placeholder merkle root 'time': int(time.time()), 'bits': bits, # Use converted bits 'height': int(height), # Ensure height is integer 'target': current_target # Calculate based on current difficulty } # Update cache self._template_cache = template self._last_cache_time = current_time return self._template_cache except Exception as e: logging.error(f"Error getting block template: {str(e)}") # Return fallback template return { 'version': 2, 'previousblockhash': '0' * 64, 'merkleroot': '0' * 64, 'time': int(time.time()), 'bits': '1d00ffff', 'height': 0, 'target': int('00000000FFFF0000000000000000000000000000000000000000000000000000', 16) } if not stats: raise Exception("Failed to get network stats") network_difficulty = float(stats['difficulty']) bits = stats.get('bits', '1d00ffff') # Default to testnet bits if missing # Parse bits to target bits_int = int(bits, 16) # Convert hex bits to int exp = ((bits_int >> 24) & 0xff) coeff = bits_int & 0x00ffffff target = coeff * (2 ** (8 * (exp - 3))) print(f"Mining at difficulty: {network_difficulty}") print(f"Network target: {hex(target)}") # Create proper coinbase input script block_height_hex = hex(height)[2:].zfill(6) # BIP34: Block height coinbase_script = ( "03" + # Push 3 bytes block_height_hex + # BIP34: Block height "0000000000000000" + # Extra nonce space "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/ ) # Create coinbase transaction coinbase_tx = { 'version': 1, 'vin': [{ 'txid': '0' * 64, # Null hash for coinbase 'vout': 0xFFFFFFFF, # -1 (4 bytes) for coinbase 'scriptSig': coinbase_script, # Block height + extra nonce + miner tag 'sequence': 0xFFFFFFFF }], 'vout': [{ 'value': 625000000, # 6.25 BTC reward 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac' # P2PKH to miner address }] } # Construct proper block template with real network data template = { 'version': 0x20000000, # Version 2 with BIP9 bits 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming 'merkleroot': current_block['mrkl_root'], # Changed to match Bitcoin Core naming 'time': int(time.time()), # Changed to match Bitcoin Core naming 'bits': bits_int, # Using parsed bits value 'height': height, 'target': target, 'difficulty': network_difficulty, # Changed to match Bitcoin Core naming 'coinbasetx': coinbase_tx, # Changed to match Bitcoin Core naming 'sizelimit': 4000000, # Changed to match Bitcoin Core naming 'transactions': [] # Pending transactions (empty for now) } return template except Exception as e: print(f"Error getting block template: {str(e)}") # Get real latest block as fallback latest_url = "https://blockchain.info/latestblock" latest_response = requests.get(latest_url) if latest_response.status_code == 200: latest_data = latest_response.json() block_height = latest_data['height'] prev_block = latest_data['hash'] else: block_height = 800_000 prev_block = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' # Get real network difficulty even in fallback diff_url = "https://blockchain.info/q/getdifficulty" try: diff_response = requests.get(diff_url) if diff_response.status_code == 200: network_difficulty = float(diff_response.text) target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty) else: target = 0x00000000ffff0000000000000000000000000000000000000000000000000000 except: target = 0x00000000ffff0000000000000000000000000000000000000000000000000000 template = { 'version': 0x20000000, 'previous_block': prev_block, 'merkle_root': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', 'timestamp': int(time.time()), 'bits': 0x1d00ffff, 'target': target, 'height': block_height, 'network_difficulty': network_difficulty if 'network_difficulty' in locals() else None } return template try: # Get latest block info response = requests.get(self.bitcoin_network.latest_block_url) if response.status_code != 200: raise Exception("Failed to get latest block") latest = response.json() # Construct proper block template with real network data template = { 'version': 0x20000000, # Version 2 with BIP9 bits 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming 'merkleroot': '0' * 64, # Will be calculated from transactions 'time': int(time.time()), # Current time 'bits': bits_int, # Using parsed bits value 'height': height, 'target': target, 'difficulty': network_difficulty, 'coinbasetx': coinbase_tx, 'sizelimit': 4000000, # 4MB block size limit 'transactions': [] # Pending transactions (empty for now) } return template except Exception as e: print(f"Error getting block template: {str(e)}") # Create fallback template template = { 'version': 0x20000000, # Version 2 with BIP9 bits 'previousblockhash': '000000000000000000024bead8df69990852c202db0e0097c1a12ea637d7e96d', 'merkleroot': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', 'time': int(time.time()), 'bits': 0x1d00ffff, 'target': 0x00000000ffff0000000000000000000000000000000000000000000000000000, 'height': 2_500_000, 'difficulty': 1.0, 'coinbasetx': { 'version': 1, 'vin': [{ 'txid': '0' * 64, 'vout': 0xFFFFFFFF, 'scriptSig': '03' + hex(2_500_000)[2:].zfill(6) + '0000000000000000', 'sequence': 0xFFFFFFFF }], 'vout': [{ 'value': 625000000, 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac' }] }, 'sizelimit': 4000000, 'transactions': [] } return template def submit_block(self, block_header: bytes, nonce: int) -> bool: """Submit found block to network""" try: # Get current template template = self.get_block_template() # Create block data starting with header including nonce block_data = bytearray(block_header[:-4] + struct.pack(' int: """Convert compact bits to target""" bits = int(bits, 16) shift = (bits >> 24) & 0xff target = (bits & 0x00ffffff) * (2 ** (8 * (shift - 3))) return target