| """
|
| 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
|
|
|
|
|
| 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"
|
| self.node = "seed.bitcoin.sipa.be"
|
| self.is_mainnet = True
|
|
|
| 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"
|
|
|
| def connect(self) -> bool:
|
| """Connect to Bitcoin mainnet"""
|
| try:
|
|
|
| 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:
|
|
|
| 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")
|
|
|
| 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}")
|
|
|
|
|
| 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}")
|
|
|
|
|
| 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)}")
|
|
|
|
|
| bits = 0x1d00ffff
|
|
|
|
|
| exp = ((bits >> 24) & 0xff)
|
| coeff = bits & 0x00ffffff
|
| current_target = coeff * (2 ** (8 * (exp - 3)))
|
|
|
|
|
| template = {
|
| 'version': 2,
|
| 'previousblockhash': current_block,
|
| 'merkleroot': '0' * 64,
|
| 'time': int(time.time()),
|
| 'bits': bits,
|
| 'target': current_target,
|
| 'height': height
|
| }
|
|
|
| logging.debug(f"Target calculated from bits {hex(bits)}: {hex(current_target)}")
|
|
|
|
|
| template = {
|
| 'version': 2,
|
| 'previousblockhash': current_block,
|
| 'merkleroot': '0' * 64,
|
| 'time': int(time.time()),
|
| 'bits': bits,
|
| 'height': int(height),
|
| 'target': current_target
|
| }
|
|
|
|
|
| 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)}")
|
|
|
|
|
| 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:
|
|
|
| network_difficulty = 137533144484879.19
|
| 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}")
|
|
|
| network_difficulty = 137533144484879.19
|
| 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)}")
|
|
|
|
|
| block_height_hex = hex(height)[2:].zfill(6)
|
| coinbase_script = (
|
| "03" +
|
| block_height_hex +
|
| "0000000000000000" +
|
| "2f4d696e656420627920426974436f696e2d436f70696c6f742f"
|
| )
|
|
|
|
|
| coinbase_tx = {
|
| 'version': 1,
|
| 'vin': [{
|
| 'txid': '0' * 64,
|
| 'vout': 0xFFFFFFFF,
|
| 'scriptSig': coinbase_script,
|
| 'sequence': 0xFFFFFFFF
|
| }],
|
| 'vout': [{
|
| 'value': 625000000,
|
| 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac'
|
| }]
|
| }
|
|
|
|
|
| template = {
|
| 'version': 0x20000000,
|
| 'previousblockhash': prev_block,
|
| 'merkleroot': current_block['mrkl_root'],
|
| 'time': int(time.time()),
|
| 'bits': bits_int,
|
| 'height': height,
|
| 'target': target,
|
| 'difficulty': network_difficulty,
|
| 'coinbasetx': coinbase_tx,
|
| 'sizelimit': 4000000,
|
| 'transactions': []
|
| }
|
|
|
| return template
|
|
|
| except Exception as e:
|
| logging.error(f"Error getting block template: {str(e)}")
|
|
|
| network_difficulty = 137533144484879.19
|
| 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
|
| prev_block = "00000000000000000000635542f008dfb9ba9f4d25e53539c62918aa5c5b852a"
|
|
|
|
|
| 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:
|
|
|
| response = requests.get(self.bitcoin_network.latest_block_url)
|
| if response.status_code != 200:
|
| raise Exception("Failed to get latest block")
|
|
|
| latest = response.json()
|
|
|
| template = {
|
| 'version': 0x20000000,
|
| 'previousblockhash': prev_block,
|
| 'merkleroot': '0' * 64,
|
| 'time': int(time.time()),
|
| 'bits': bits_int,
|
| 'height': height,
|
| 'target': target,
|
| 'difficulty': network_difficulty,
|
| 'coinbasetx': coinbase_tx,
|
| 'sizelimit': 4000000,
|
| 'transactions': []
|
| }
|
| return template
|
| except Exception as e:
|
| print(f"Error getting block template: {str(e)}")
|
|
|
| template = {
|
| 'version': 0x20000000,
|
| '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:
|
|
|
| template = self.get_block_template()
|
|
|
|
|
| block_data = bytearray(block_header[:-4] + struct.pack('<I', nonce))
|
|
|
|
|
| block_data.extend(bytes([1]))
|
|
|
|
|
| block_height = template['height']
|
| block_height_hex = hex(block_height)[2:].zfill(6)
|
|
|
|
|
| coinbase_script = bytes.fromhex(
|
| "03" +
|
| block_height_hex +
|
| "0000000000000000" +
|
| "2f4d696e656420627920426974436f696e2d436f70696c6f742f"
|
| )
|
|
|
|
|
| tx_data = struct.pack('<I', 1)
|
|
|
|
|
| tx_data += bytes([1])
|
|
|
|
|
| tx_data += b'\x00' * 32
|
| tx_data += struct.pack('<I', 0xFFFFFFFF)
|
|
|
|
|
| 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
|
| tx_data += struct.pack('<I', 0xFFFFFFFF)
|
|
|
|
|
| tx_data += bytes([1])
|
|
|
|
|
| tx_data += struct.pack('<Q', 625000000)
|
|
|
|
|
|
|
| from base58 import b58decode_check
|
| decoded = b58decode_check(self.wallet_address)
|
| pubkey_hash = decoded[1:]
|
|
|
|
|
| script_pubkey = bytes([0x76, 0xa9, 0x14]) + pubkey_hash + bytes([0x88, 0xac])
|
| tx_data += bytes([len(script_pubkey)])
|
| tx_data += script_pubkey
|
|
|
|
|
| tx_data += struct.pack('<I', 0)
|
|
|
|
|
| block_data.extend(tx_data)
|
|
|
|
|
| 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 |