LAYS / network_integration.py
Fred808's picture
Upload 11 files
4a0b443 verified
"""
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