Spaces:
Paused
Paused
| """ | |
| 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://blockchain.info" # Changed to more reliable API | |
| self.node = "seed.bitcoin.sipa.be" # Bitcoin mainnet seed node | |
| self.is_mainnet = True # Force mainnet mode | |
| # 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 and difficulty | |
| logging.debug("Fetching network stats...") | |
| diff_response = requests.get("https://blockchain.info/q/getdifficulty") | |
| if diff_response.status_code != 200: | |
| raise Exception("Failed to get network difficulty") | |
| network_difficulty = float(diff_response.text) | |
| logging.info(f"Current network difficulty: {network_difficulty}") | |
| # Calculate target from difficulty | |
| max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 | |
| target = int(max_target / network_difficulty) | |
| bits = f"{0x1d:02x}{(target >> 208) & 0xffffff:06x}" | |
| logging.debug(f"Target calculated from difficulty: {hex(target)}") | |
| # Use fixed bits for target calculation | |
| bits = 0x1d00ffff # Standard Bitcoin difficulty 1 target | |
| # Calculate target from bits | |
| exp = ((bits >> 24) & 0xff) | |
| coeff = bits & 0x00ffffff | |
| current_target = coeff * (2 ** (8 * (exp - 3))) | |
| # Create block template | |
| template = { | |
| 'version': 2, | |
| 'previousblockhash': current_block, | |
| 'merkleroot': '0' * 64, # Placeholder merkle root | |
| 'time': int(time.time()), | |
| 'bits': bits, | |
| 'target': current_target, | |
| 'height': height | |
| } | |
| logging.debug(f"Target calculated from bits {hex(bits)}: {hex(current_target)}") | |
| # 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, # Original bits value | |
| 'height': int(height), # Ensure height is integer | |
| 'target': current_target # Correctly calculated target from bits | |
| } | |
| # 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 | |
| # Get real mainnet difficulty from blockchain.info | |
| 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) | |
| bits = hex(int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty))[2:] | |
| target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty) | |
| logging.info(f"Got mainnet difficulty: {network_difficulty}") | |
| else: | |
| # Use more reasonable fallback difficulty for mainnet | |
| network_difficulty = 137533144484879.19 # Recent mainnet difficulty | |
| target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty) | |
| bits = f"{0x1d:02x}{target & 0xffffff:06x}" | |
| except Exception as e: | |
| logging.error(f"Error getting mainnet difficulty: {e}") | |
| # Use more reasonable fallback difficulty for mainnet | |
| network_difficulty = 137533144484879.19 # Recent mainnet difficulty | |
| target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty) | |
| bits = f"{0x1d:02x}{target & 0xffffff:06x}" | |
| return { | |
| 'version': 2, | |
| 'previousblockhash': '0' * 64, | |
| 'merkleroot': '0' * 64, | |
| 'time': int(time.time()), | |
| 'bits': bits, | |
| 'height': 0, | |
| 'target': target | |
| } | |
| 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: | |
| logging.error(f"Error getting block template: {str(e)}") | |
| # Use fallback difficulty and target | |
| network_difficulty = 137533144484879.19 # Recent mainnet difficulty | |
| max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 | |
| target = int(max_target / network_difficulty) | |
| bits = f"{0x1d:02x}{(target >> 208) & 0xffffff:06x}" | |
| logging.info(f"Using fallback difficulty: {network_difficulty}") | |
| block_height = 917362 # Recent block height | |
| prev_block = "00000000000000000000635542f008dfb9ba9f4d25e53539c62918aa5c5b852a" # Recent block hash | |
| # 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('<I', nonce)) | |
| # Add transaction count varint (1 for coinbase only) | |
| block_data.extend(bytes([1])) | |
| # Create proper coinbase transaction | |
| block_height = template['height'] | |
| block_height_hex = hex(block_height)[2:].zfill(6) # BIP34: Block height | |
| # Create proper coinbase script | |
| coinbase_script = bytes.fromhex( | |
| "03" + # Push 3 bytes | |
| block_height_hex + # Block height (BIP34) | |
| "0000000000000000" + # Extra nonce | |
| "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/ | |
| ) | |
| # Start serializing the coinbase transaction | |
| tx_data = struct.pack('<I', 1) # Version 1 | |
| # Input count (always 1 for coinbase) | |
| tx_data += bytes([1]) | |
| # Coinbase input with proper null txid (must be exactly 32 bytes) | |
| tx_data += b'\x00' * 32 # Previous txid (null for coinbase) | |
| tx_data += struct.pack('<I', 0xFFFFFFFF) # Previous output index (-1 for coinbase) | |
| # Create proper coinbase input script with proper length prefix | |
| script_len = len(coinbase_script) | |
| if script_len < 0xfd: | |
| tx_data += bytes([script_len]) | |
| elif script_len <= 0xffff: | |
| tx_data += bytes([0xfd]) + struct.pack('<H', script_len) | |
| elif script_len <= 0xffffffff: | |
| tx_data += bytes([0xfe]) + struct.pack('<I', script_len) | |
| else: | |
| tx_data += bytes([0xff]) + struct.pack('<Q', script_len) | |
| tx_data += coinbase_script # Coinbase script | |
| tx_data += struct.pack('<I', 0xFFFFFFFF) # Sequence | |
| # Output count (1 output paying the miner) | |
| tx_data += bytes([1]) | |
| # Miner's reward output (6.25 BTC) | |
| tx_data += struct.pack('<Q', 625000000) # Value in satoshis | |
| # Create proper P2PKH script for payout | |
| # First decode the base58 address to get the pubkey hash | |
| from base58 import b58decode_check | |
| decoded = b58decode_check(self.wallet_address) | |
| pubkey_hash = decoded[1:] # Skip version byte | |
| # Build P2PKH script | |
| script_pubkey = bytes([0x76, 0xa9, 0x14]) + pubkey_hash + bytes([0x88, 0xac]) | |
| tx_data += bytes([len(script_pubkey)]) # Script length | |
| tx_data += script_pubkey # P2PKH script | |
| # Add locktime | |
| tx_data += struct.pack('<I', 0) # nLockTime | |
| # Add serialized coinbase transaction to block | |
| block_data.extend(tx_data) | |
| # Submit block using blockchain.info API | |
| submit_url = 'https://api.blockchain.info/haskoin-store/btc/block' | |
| headers = {'Content-Type': 'application/x-www-form-urlencoded'} | |
| response = requests.post(submit_url, data={'block': block_data.hex()}, headers=headers) | |
| if response.status_code == 200: | |
| print(f"Block successfully submitted!") | |
| logging.info("Block submission successful") | |
| return True | |
| elif response.status_code == 400 and 'bad-txns-vin-empty' in response.text: | |
| print("Block rejected: Invalid coinbase transaction structure") | |
| logging.error("Block rejected due to invalid coinbase transaction") | |
| return False | |
| else: | |
| error_msg = response.text if response.text else f"Status code: {response.status_code}" | |
| print(f"Block submission failed: {error_msg}") | |
| logging.error(f"Block submission failed: {error_msg}") | |
| return False | |
| except Exception as e: | |
| print(f"Error submitting block: {str(e)}") | |
| return False | |
| try: | |
| block_hash = hashlib.sha256(hashlib.sha256(block_header).digest()).digest() | |
| return self.bitcoin_network.submit_block(block_header, nonce) | |
| except Exception as e: | |
| print(f"Block submission error: {e}") | |
| return False | |
| def _bits_to_target(self, bits: str) -> int: | |
| """Convert compact bits to target""" | |
| bits = int(bits, 16) | |
| shift = (bits >> 24) & 0xff | |
| target = (bits & 0x00ffffff) * (2 ** (8 * (shift - 3))) | |
| return target |