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 | |
| } | |
| 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() | |
| # Verify the block hash first | |
| full_header = block_header[:-4] + struct.pack('<I', nonce) | |
| block_hash = hashlib.sha256(hashlib.sha256(full_header).digest()).digest() | |
| block_hash_hex = block_hash.hex() | |
| block_hash_int = int.from_bytes(block_hash, 'little') | |
| # Log detailed hash information | |
| logging.info(f"Block hash: {block_hash_hex}") | |
| logging.info(f"Leading zeros: {len(block_hash_hex) - len(block_hash_hex.lstrip('0'))}") | |
| logging.info(f"Hash value: {int(block_hash_hex, 16)}") | |
| logging.info(f"Target value: {template['target']}") | |
| if block_hash_int >= template['target']: | |
| logging.error("Block hash doesn't meet target requirement") | |
| logging.error(f"Hash/Target ratio: {block_hash_int / template['target']:.2f}") | |
| return False | |
| # Create block data starting with header including nonce | |
| block_data = bytearray(full_header) | |
| # Add transaction count varint (1 for coinbase only) | |
| block_data.extend(bytes([1])) | |
| # Create proper coinbase transaction with our wallet address | |
| block_height = template['height'] | |
| block_height_hex = hex(block_height)[2:].zfill(6) # BIP34: Block height | |
| # Create proper coinbase script with required elements | |
| coinbase_script = ( | |
| bytes([3]) + # Push 3 bytes (block height) | |
| bytes.fromhex(block_height_hex) + # Block height (BIP34) | |
| bytes.fromhex("0000000000000000") + # Extra nonce space | |
| b"/Mined by Elias/" # Miner tag | |
| ) | |
| # Import required for base58 decoding | |
| from base58 import b58decode_check | |
| # Decode wallet address to get public key hash | |
| try: | |
| decoded = b58decode_check(self.wallet_address) | |
| pubkey_hash = decoded[1:] # Remove version byte | |
| except Exception as e: | |
| logging.error(f"Error decoding wallet address: {e}") | |
| return False | |
| # Create complete coinbase transaction | |
| coinbase_tx = ( | |
| struct.pack('<I', 1) + # Version | |
| bytes([1]) + # Input count | |
| bytes.fromhex('0' * 64) + # Previous tx hash (null for coinbase) | |
| struct.pack('<I', 0xFFFFFFFF) + # Previous output index | |
| bytes([len(coinbase_script)]) + # Script length | |
| coinbase_script + # Coinbase script | |
| struct.pack('<I', 0xFFFFFFFF) + # Sequence | |
| bytes([1]) + # Output count | |
| struct.pack('<Q', 625000000) + # 6.25 BTC reward in satoshis | |
| bytes([25]) + # Output script length (25 bytes for P2PKH) | |
| bytes([ | |
| 0x76, # OP_DUP | |
| 0xa9, # OP_HASH160 | |
| 0x14 # Push 20 bytes | |
| ]) + | |
| pubkey_hash + # Public key hash from decoded address | |
| bytes([ | |
| 0x88, # OP_EQUALVERIFY | |
| 0xac # OP_CHECKSIG | |
| ]) + | |
| struct.pack('<I', 0) # Locktime | |
| ) | |
| # Add coinbase transaction to block | |
| block_data.extend(coinbase_tx) | |
| # Add empty witness commitment | |
| block_data.extend(bytes([0])) # No witness data | |
| # Submit to more reliable nodes | |
| successful = False | |
| nodes = [ | |
| "https://btc.getblock.io/mainnet/", # Primary | |
| "https://blockchain.info/pushtx", # Backup 1 | |
| "https://api.bitcore.io/api/BTC/mainnet/tx/send", # Backup 2 | |
| self.api_base + "/submitblock" # Original endpoint as fallback | |
| ] | |
| for node in nodes: | |
| try: | |
| logging.info(f"Attempting submission to {node}") | |
| response = requests.post( | |
| node, | |
| data={'block': block_data.hex()}, | |
| headers={'Content-Type': 'application/x-www-form-urlencoded'}, | |
| timeout=10 | |
| ) | |
| if response.status_code == 200: | |
| logging.info(f"Successfully submitted block {block_hash.hex()}") | |
| logging.info(f"Block reward sent to {self.wallet_address}") | |
| successful = True | |
| break | |
| else: | |
| logging.warning(f"Submission failed for {node}: {response.text}") | |
| except Exception as e: | |
| logging.warning(f"Error submitting to {node}: {e}") | |
| continue | |
| return successful | |
| except Exception as e: | |
| logging.error(f"Error submitting block: {str(e)}") | |
| return False | |
| # 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 |