favoredone commited on
Commit
645b57e
·
verified ·
1 Parent(s): fdf3524

Update network_integration.py

Browse files
Files changed (1) hide show
  1. network_integration.py +398 -366
network_integration.py CHANGED
@@ -1,367 +1,399 @@
1
- """
2
- Integrates the mining system with Bitcoin mainnet
3
- """
4
- from typing import Dict, Any, Optional
5
- import time
6
- import requests
7
- import hashlib
8
- import json
9
- import logging
10
- import struct
11
-
12
- # Configure detailed logging
13
- logging.basicConfig(
14
- level=logging.DEBUG,
15
- format='%(asctime)s - %(levelname)s - %(message)s',
16
- handlers=[
17
- logging.FileHandler('network_debug.log'),
18
- logging.StreamHandler()
19
- ]
20
- )
21
-
22
- class NetworkIntegration:
23
- def __init__(self, wallet_address: str = None):
24
- self.api_base = "https://api.bitaps.com/btc/v1"
25
- self.node = "seed.bitcoin.sipa.be" # Bitcoin mainnet seed node
26
- # Use the provided wallet address or load from my_wallet.json
27
- if wallet_address:
28
- self.wallet_address = wallet_address
29
- else:
30
- try:
31
- with open('my_wallet.json', 'r') as f:
32
- wallet_data = json.load(f)
33
- self.wallet_address = wallet_data['address']
34
- print(f"Using wallet address: {self.wallet_address}")
35
- except Exception as e:
36
- print(f"Error loading wallet: {e}")
37
- self.wallet_address = "1Ks4WtCEK96BaBF7HSuCGt3rEpVKPqcJKf" # Your default address
38
-
39
- def connect(self) -> bool:
40
- """Connect to Bitcoin mainnet"""
41
- try:
42
- # Test connection by getting latest block
43
- response = requests.get(f"{self.api_base}/blockchain/blocks/last")
44
- return response.status_code == 200
45
- except Exception as e:
46
- print(f"Failed to connect to mainnet: {e}")
47
- return False
48
-
49
- def get_block_template(self) -> Dict[str, Any]:
50
- """Get current block template from mainnet"""
51
- try:
52
- # Cache the blockchain API response for 5 minutes
53
- current_time = time.time()
54
- if not hasattr(self, '_template_cache') or current_time - self._last_cache_time > 300:
55
- logging.debug("Cache expired, fetching new block template")
56
- # Get latest block info from a more reliable API
57
- response = requests.get("https://blockchain.info/latestblock")
58
- logging.debug(f"Latest block API response status: {response.status_code}")
59
-
60
- if response.status_code != 200:
61
- logging.error(f"Failed to get latest block. Status code: {response.status_code}")
62
- if hasattr(self, '_template_cache'):
63
- logging.info("Using cached template")
64
- return self._template_cache
65
- raise Exception("Failed to get latest block")
66
-
67
- latest = response.json()
68
- logging.debug(f"Latest block response: {latest}")
69
-
70
- height = latest['height']
71
- current_block = latest['hash']
72
- logging.info(f"Current block height: {height}, hash: {current_block}")
73
-
74
- # Get current network stats
75
- logging.debug("Fetching network stats...")
76
- stats_response = requests.get("https://blockchain.info/stats?format=json")
77
- logging.debug(f"Network stats API response status: {stats_response.status_code}")
78
- stats = stats_response.json()
79
-
80
- # Convert difficulty to target
81
- difficulty = float(stats.get('difficulty', 1))
82
- max_target = int('00000000FFFF0000000000000000000000000000000000000000000000000000', 16)
83
- current_target = int(max_target / difficulty)
84
-
85
- bits = stats.get('bits', '1d00ffff')
86
- if isinstance(bits, str):
87
- bits = int(bits, 16)
88
-
89
- # Construct template with required fields
90
- template = {
91
- 'version': 2, # Current Bitcoin version
92
- 'previousblockhash': current_block, # Use current block as previous for next block
93
- 'merkleroot': '0' * 64, # Placeholder merkle root
94
- 'time': int(time.time()),
95
- 'bits': bits, # Use converted bits
96
- 'height': int(height), # Ensure height is integer
97
- 'target': current_target # Calculate based on current difficulty
98
- }
99
-
100
- # Update cache
101
- self._template_cache = template
102
- self._last_cache_time = current_time
103
-
104
- return self._template_cache
105
-
106
- except Exception as e:
107
- logging.error(f"Error getting block template: {str(e)}")
108
- # Return fallback template
109
- return {
110
- 'version': 2,
111
- 'previousblockhash': '0' * 64,
112
- 'merkleroot': '0' * 64,
113
- 'time': int(time.time()),
114
- 'bits': '1d00ffff',
115
- 'height': 0,
116
- 'target': int('00000000FFFF0000000000000000000000000000000000000000000000000000', 16)
117
- }
118
- if not stats:
119
- raise Exception("Failed to get network stats")
120
-
121
- network_difficulty = float(stats['difficulty'])
122
- bits = stats.get('bits', '1d00ffff') # Default to testnet bits if missing
123
-
124
- # Parse bits to target
125
- bits_int = int(bits, 16) # Convert hex bits to int
126
- exp = ((bits_int >> 24) & 0xff)
127
- coeff = bits_int & 0x00ffffff
128
- target = coeff * (2 ** (8 * (exp - 3)))
129
-
130
- print(f"Mining at difficulty: {network_difficulty}")
131
- print(f"Network target: {hex(target)}")
132
-
133
- # Create proper coinbase input script
134
- block_height_hex = hex(height)[2:].zfill(6) # BIP34: Block height
135
- coinbase_script = (
136
- "03" + # Push 3 bytes
137
- block_height_hex + # BIP34: Block height
138
- "0000000000000000" + # Extra nonce space
139
- "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/
140
- )
141
-
142
- # Create coinbase transaction
143
- coinbase_tx = {
144
- 'version': 1,
145
- 'vin': [{
146
- 'txid': '0' * 64, # Null hash for coinbase
147
- 'vout': 0xFFFFFFFF, # -1 (4 bytes) for coinbase
148
- 'scriptSig': coinbase_script, # Block height + extra nonce + miner tag
149
- 'sequence': 0xFFFFFFFF
150
- }],
151
- 'vout': [{
152
- 'value': 625000000, # 6.25 BTC reward
153
- 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac' # P2PKH to miner address
154
- }]
155
- }
156
-
157
- # Construct proper block template with real network data
158
- template = {
159
- 'version': 0x20000000, # Version 2 with BIP9 bits
160
- 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming
161
- 'merkleroot': current_block['mrkl_root'], # Changed to match Bitcoin Core naming
162
- 'time': int(time.time()), # Changed to match Bitcoin Core naming
163
- 'bits': bits_int, # Using parsed bits value
164
- 'height': height,
165
- 'target': target,
166
- 'difficulty': network_difficulty, # Changed to match Bitcoin Core naming
167
- 'coinbasetx': coinbase_tx, # Changed to match Bitcoin Core naming
168
- 'sizelimit': 4000000, # Changed to match Bitcoin Core naming
169
- 'transactions': [] # Pending transactions (empty for now)
170
- }
171
-
172
- return template
173
-
174
- except Exception as e:
175
- print(f"Error getting block template: {str(e)}")
176
- # Get real latest block as fallback
177
- latest_url = "https://blockchain.info/latestblock"
178
- latest_response = requests.get(latest_url)
179
- if latest_response.status_code == 200:
180
- latest_data = latest_response.json()
181
- block_height = latest_data['height']
182
- prev_block = latest_data['hash']
183
- else:
184
- block_height = 800_000
185
- prev_block = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
186
-
187
- # Get real network difficulty even in fallback
188
- diff_url = "https://blockchain.info/q/getdifficulty"
189
- try:
190
- diff_response = requests.get(diff_url)
191
- if diff_response.status_code == 200:
192
- network_difficulty = float(diff_response.text)
193
- target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty)
194
- else:
195
- target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
196
- except:
197
- target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
198
-
199
- template = {
200
- 'version': 0x20000000,
201
- 'previous_block': prev_block,
202
- 'merkle_root': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
203
- 'timestamp': int(time.time()),
204
- 'bits': 0x1d00ffff,
205
- 'target': target,
206
- 'height': block_height,
207
- 'network_difficulty': network_difficulty if 'network_difficulty' in locals() else None
208
- }
209
- return template
210
- try:
211
- # Get latest block info
212
- response = requests.get(self.bitcoin_network.latest_block_url)
213
- if response.status_code != 200:
214
- raise Exception("Failed to get latest block")
215
-
216
- latest = response.json()
217
- # Construct proper block template with real network data
218
- template = {
219
- 'version': 0x20000000, # Version 2 with BIP9 bits
220
- 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming
221
- 'merkleroot': '0' * 64, # Will be calculated from transactions
222
- 'time': int(time.time()), # Current time
223
- 'bits': bits_int, # Using parsed bits value
224
- 'height': height,
225
- 'target': target,
226
- 'difficulty': network_difficulty,
227
- 'coinbasetx': coinbase_tx,
228
- 'sizelimit': 4000000, # 4MB block size limit
229
- 'transactions': [] # Pending transactions (empty for now)
230
- }
231
- return template
232
- except Exception as e:
233
- print(f"Error getting block template: {str(e)}")
234
- # Create fallback template
235
- template = {
236
- 'version': 0x20000000, # Version 2 with BIP9 bits
237
- 'previousblockhash': '000000000000000000024bead8df69990852c202db0e0097c1a12ea637d7e96d',
238
- 'merkleroot': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
239
- 'time': int(time.time()),
240
- 'bits': 0x1d00ffff,
241
- 'target': 0x00000000ffff0000000000000000000000000000000000000000000000000000,
242
- 'height': 2_500_000,
243
- 'difficulty': 1.0,
244
- 'coinbasetx': {
245
- 'version': 1,
246
- 'vin': [{
247
- 'txid': '0' * 64,
248
- 'vout': 0xFFFFFFFF,
249
- 'scriptSig': '03' + hex(2_500_000)[2:].zfill(6) + '0000000000000000',
250
- 'sequence': 0xFFFFFFFF
251
- }],
252
- 'vout': [{
253
- 'value': 625000000,
254
- 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac'
255
- }]
256
- },
257
- 'sizelimit': 4000000,
258
- 'transactions': []
259
- }
260
- return template
261
-
262
- def submit_block(self, block_header: bytes, nonce: int) -> bool:
263
- """Submit found block to network"""
264
- try:
265
- # Get current template
266
- template = self.get_block_template()
267
-
268
- # Create block data starting with header including nonce
269
- block_data = bytearray(block_header[:-4] + struct.pack('<I', nonce))
270
-
271
- # Add transaction count varint (1 for coinbase only)
272
- block_data.extend(bytes([1]))
273
-
274
- # Create proper coinbase transaction
275
- block_height = template['height']
276
- block_height_hex = hex(block_height)[2:].zfill(6) # BIP34: Block height
277
-
278
- # Create proper coinbase script
279
- coinbase_script = bytes.fromhex(
280
- "03" + # Push 3 bytes
281
- block_height_hex + # Block height (BIP34)
282
- "0000000000000000" + # Extra nonce
283
- "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/
284
- )
285
-
286
- # Start serializing the coinbase transaction
287
- tx_data = struct.pack('<I', 1) # Version 1
288
-
289
- # Input count (always 1 for coinbase)
290
- tx_data += bytes([1])
291
-
292
- # Coinbase input with proper null txid (must be exactly 32 bytes)
293
- tx_data += b'\x00' * 32 # Previous txid (null for coinbase)
294
- tx_data += struct.pack('<I', 0xFFFFFFFF) # Previous output index (-1 for coinbase)
295
-
296
- # Create proper coinbase input script with proper length prefix
297
- script_len = len(coinbase_script)
298
- if script_len < 0xfd:
299
- tx_data += bytes([script_len])
300
- elif script_len <= 0xffff:
301
- tx_data += bytes([0xfd]) + struct.pack('<H', script_len)
302
- elif script_len <= 0xffffffff:
303
- tx_data += bytes([0xfe]) + struct.pack('<I', script_len)
304
- else:
305
- tx_data += bytes([0xff]) + struct.pack('<Q', script_len)
306
-
307
- tx_data += coinbase_script # Coinbase script
308
- tx_data += struct.pack('<I', 0xFFFFFFFF) # Sequence
309
-
310
- # Output count (1 output paying the miner)
311
- tx_data += bytes([1])
312
-
313
- # Miner's reward output (6.25 BTC)
314
- tx_data += struct.pack('<Q', 625000000) # Value in satoshis
315
-
316
- # Create proper P2PKH script for payout
317
- # First decode the base58 address to get the pubkey hash
318
- from base58 import b58decode_check
319
- decoded = b58decode_check(self.wallet_address)
320
- pubkey_hash = decoded[1:] # Skip version byte
321
-
322
- # Build P2PKH script
323
- script_pubkey = bytes([0x76, 0xa9, 0x14]) + pubkey_hash + bytes([0x88, 0xac])
324
- tx_data += bytes([len(script_pubkey)]) # Script length
325
- tx_data += script_pubkey # P2PKH script
326
-
327
- # Add locktime
328
- tx_data += struct.pack('<I', 0) # nLockTime
329
-
330
- # Add serialized coinbase transaction to block
331
- block_data.extend(tx_data)
332
-
333
- # Submit block using blockchain.info API
334
- submit_url = 'https://api.blockchain.info/haskoin-store/btc/block'
335
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
336
- response = requests.post(submit_url, data={'block': block_data.hex()}, headers=headers)
337
-
338
- if response.status_code == 200:
339
- print(f"Block successfully submitted!")
340
- logging.info("Block submission successful")
341
- return True
342
- elif response.status_code == 400 and 'bad-txns-vin-empty' in response.text:
343
- print("Block rejected: Invalid coinbase transaction structure")
344
- logging.error("Block rejected due to invalid coinbase transaction")
345
- return False
346
- else:
347
- error_msg = response.text if response.text else f"Status code: {response.status_code}"
348
- print(f"Block submission failed: {error_msg}")
349
- logging.error(f"Block submission failed: {error_msg}")
350
- return False
351
-
352
- except Exception as e:
353
- print(f"Error submitting block: {str(e)}")
354
- return False
355
- try:
356
- block_hash = hashlib.sha256(hashlib.sha256(block_header).digest()).digest()
357
- return self.bitcoin_network.submit_block(block_header, nonce)
358
- except Exception as e:
359
- print(f"Block submission error: {e}")
360
- return False
361
-
362
- def _bits_to_target(self, bits: str) -> int:
363
- """Convert compact bits to target"""
364
- bits = int(bits, 16)
365
- shift = (bits >> 24) & 0xff
366
- target = (bits & 0x00ffffff) * (2 ** (8 * (shift - 3)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  return target
 
1
+ """
2
+ Integrates the mining system with Bitcoin mainnet
3
+ """
4
+ from typing import Dict, Any, Optional
5
+ import time
6
+ import requests
7
+ import hashlib
8
+ import json
9
+ import logging
10
+ import struct
11
+
12
+ # Configure detailed logging
13
+ logging.basicConfig(
14
+ level=logging.DEBUG,
15
+ format='%(asctime)s - %(levelname)s - %(message)s',
16
+ handlers=[
17
+ logging.FileHandler('network_debug.log'),
18
+ logging.StreamHandler()
19
+ ]
20
+ )
21
+
22
+ class NetworkIntegration:
23
+ def __init__(self, wallet_address: str = None):
24
+ self.api_base = "https://blockchain.info" # Changed to more reliable API
25
+ self.node = "seed.bitcoin.sipa.be" # Bitcoin mainnet seed node
26
+ self.is_mainnet = True # Force mainnet mode
27
+ # Use the provided wallet address or load from my_wallet.json
28
+ if wallet_address:
29
+ self.wallet_address = wallet_address
30
+ else:
31
+ try:
32
+ with open('my_wallet.json', 'r') as f:
33
+ wallet_data = json.load(f)
34
+ self.wallet_address = wallet_data['address']
35
+ print(f"Using wallet address: {self.wallet_address}")
36
+ except Exception as e:
37
+ print(f"Error loading wallet: {e}")
38
+ self.wallet_address = "1Ks4WtCEK96BaBF7HSuCGt3rEpVKPqcJKf" # Your default address
39
+
40
+ def connect(self) -> bool:
41
+ """Connect to Bitcoin mainnet"""
42
+ try:
43
+ # Test connection by getting latest block
44
+ response = requests.get(f"{self.api_base}/blockchain/blocks/last")
45
+ return response.status_code == 200
46
+ except Exception as e:
47
+ print(f"Failed to connect to mainnet: {e}")
48
+ return False
49
+
50
+ def get_block_template(self) -> Dict[str, Any]:
51
+ """Get current block template from mainnet"""
52
+ try:
53
+ # Cache the blockchain API response for 5 minutes
54
+ current_time = time.time()
55
+ if not hasattr(self, '_template_cache') or current_time - self._last_cache_time > 300:
56
+ logging.debug("Cache expired, fetching new block template")
57
+ # Get latest block info from a more reliable API
58
+ response = requests.get("https://blockchain.info/latestblock")
59
+ logging.debug(f"Latest block API response status: {response.status_code}")
60
+
61
+ if response.status_code != 200:
62
+ logging.error(f"Failed to get latest block. Status code: {response.status_code}")
63
+ if hasattr(self, '_template_cache'):
64
+ logging.info("Using cached template")
65
+ return self._template_cache
66
+ raise Exception("Failed to get latest block")
67
+
68
+ latest = response.json()
69
+ logging.debug(f"Latest block response: {latest}")
70
+
71
+ height = latest['height']
72
+ current_block = latest['hash']
73
+ logging.info(f"Current block height: {height}, hash: {current_block}")
74
+
75
+ # Get current network stats and difficulty
76
+ logging.debug("Fetching network stats...")
77
+ diff_response = requests.get("https://blockchain.info/q/getdifficulty")
78
+ if diff_response.status_code != 200:
79
+ raise Exception("Failed to get network difficulty")
80
+
81
+ network_difficulty = float(diff_response.text)
82
+ logging.info(f"Current network difficulty: {network_difficulty}")
83
+
84
+ # Calculate target from difficulty
85
+ max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
86
+ target = int(max_target / network_difficulty)
87
+ bits = f"{0x1d:02x}{(target >> 208) & 0xffffff:06x}"
88
+
89
+ logging.debug(f"Target calculated from difficulty: {hex(target)}")
90
+
91
+ # Use fixed bits for target calculation
92
+ bits = 0x1d00ffff # Standard Bitcoin difficulty 1 target
93
+
94
+ # Calculate target from bits
95
+ exp = ((bits >> 24) & 0xff)
96
+ coeff = bits & 0x00ffffff
97
+ current_target = coeff * (2 ** (8 * (exp - 3)))
98
+
99
+ # Create block template
100
+ template = {
101
+ 'version': 2,
102
+ 'previousblockhash': current_block,
103
+ 'merkleroot': '0' * 64, # Placeholder merkle root
104
+ 'time': int(time.time()),
105
+ 'bits': bits,
106
+ 'target': current_target,
107
+ 'height': height
108
+ }
109
+
110
+ logging.debug(f"Target calculated from bits {hex(bits)}: {hex(current_target)}")
111
+
112
+ # Construct template with required fields
113
+ template = {
114
+ 'version': 2, # Current Bitcoin version
115
+ 'previousblockhash': current_block, # Use current block as previous for next block
116
+ 'merkleroot': '0' * 64, # Placeholder merkle root
117
+ 'time': int(time.time()),
118
+ 'bits': bits, # Original bits value
119
+ 'height': int(height), # Ensure height is integer
120
+ 'target': current_target # Correctly calculated target from bits
121
+ }
122
+
123
+ # Update cache
124
+ self._template_cache = template
125
+ self._last_cache_time = current_time
126
+
127
+ return self._template_cache
128
+
129
+ except Exception as e:
130
+ logging.error(f"Error getting block template: {str(e)}")
131
+ # Return fallback template
132
+ # Get real mainnet difficulty from blockchain.info
133
+ diff_url = "https://blockchain.info/q/getdifficulty"
134
+ try:
135
+ diff_response = requests.get(diff_url)
136
+ if diff_response.status_code == 200:
137
+ network_difficulty = float(diff_response.text)
138
+ bits = hex(int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty))[2:]
139
+ target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty)
140
+ logging.info(f"Got mainnet difficulty: {network_difficulty}")
141
+ else:
142
+ # Use more reasonable fallback difficulty for mainnet
143
+ network_difficulty = 137533144484879.19 # Recent mainnet difficulty
144
+ target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty)
145
+ bits = f"{0x1d:02x}{target & 0xffffff:06x}"
146
+ except Exception as e:
147
+ logging.error(f"Error getting mainnet difficulty: {e}")
148
+ # Use more reasonable fallback difficulty for mainnet
149
+ network_difficulty = 137533144484879.19 # Recent mainnet difficulty
150
+ target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty)
151
+ bits = f"{0x1d:02x}{target & 0xffffff:06x}"
152
+
153
+ return {
154
+ 'version': 2,
155
+ 'previousblockhash': '0' * 64,
156
+ 'merkleroot': '0' * 64,
157
+ 'time': int(time.time()),
158
+ 'bits': bits,
159
+ 'height': 0,
160
+ 'target': target
161
+ }
162
+
163
+ print(f"Mining at difficulty: {network_difficulty}")
164
+ print(f"Network target: {hex(target)}")
165
+
166
+ # Create proper coinbase input script
167
+ block_height_hex = hex(height)[2:].zfill(6) # BIP34: Block height
168
+ coinbase_script = (
169
+ "03" + # Push 3 bytes
170
+ block_height_hex + # BIP34: Block height
171
+ "0000000000000000" + # Extra nonce space
172
+ "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/
173
+ )
174
+
175
+ # Create coinbase transaction
176
+ coinbase_tx = {
177
+ 'version': 1,
178
+ 'vin': [{
179
+ 'txid': '0' * 64, # Null hash for coinbase
180
+ 'vout': 0xFFFFFFFF, # -1 (4 bytes) for coinbase
181
+ 'scriptSig': coinbase_script, # Block height + extra nonce + miner tag
182
+ 'sequence': 0xFFFFFFFF
183
+ }],
184
+ 'vout': [{
185
+ 'value': 625000000, # 6.25 BTC reward
186
+ 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac' # P2PKH to miner address
187
+ }]
188
+ }
189
+
190
+ # Construct proper block template with real network data
191
+ template = {
192
+ 'version': 0x20000000, # Version 2 with BIP9 bits
193
+ 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming
194
+ 'merkleroot': current_block['mrkl_root'], # Changed to match Bitcoin Core naming
195
+ 'time': int(time.time()), # Changed to match Bitcoin Core naming
196
+ 'bits': bits_int, # Using parsed bits value
197
+ 'height': height,
198
+ 'target': target,
199
+ 'difficulty': network_difficulty, # Changed to match Bitcoin Core naming
200
+ 'coinbasetx': coinbase_tx, # Changed to match Bitcoin Core naming
201
+ 'sizelimit': 4000000, # Changed to match Bitcoin Core naming
202
+ 'transactions': [] # Pending transactions (empty for now)
203
+ }
204
+
205
+ return template
206
+
207
+ except Exception as e:
208
+ logging.error(f"Error getting block template: {str(e)}")
209
+ # Use fallback difficulty and target
210
+ network_difficulty = 137533144484879.19 # Recent mainnet difficulty
211
+ max_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
212
+ target = int(max_target / network_difficulty)
213
+ bits = f"{0x1d:02x}{(target >> 208) & 0xffffff:06x}"
214
+
215
+ logging.info(f"Using fallback difficulty: {network_difficulty}")
216
+ block_height = 917362 # Recent block height
217
+ prev_block = "00000000000000000000635542f008dfb9ba9f4d25e53539c62918aa5c5b852a" # Recent block hash
218
+
219
+ # Get real network difficulty even in fallback
220
+ diff_url = "https://blockchain.info/q/getdifficulty"
221
+ try:
222
+ diff_response = requests.get(diff_url)
223
+ if diff_response.status_code == 200:
224
+ network_difficulty = float(diff_response.text)
225
+ target = int((0xffff * 2**(8*(0x1d - 3))) / network_difficulty)
226
+ else:
227
+ target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
228
+ except:
229
+ target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
230
+
231
+ template = {
232
+ 'version': 0x20000000,
233
+ 'previous_block': prev_block,
234
+ 'merkle_root': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
235
+ 'timestamp': int(time.time()),
236
+ 'bits': 0x1d00ffff,
237
+ 'target': target,
238
+ 'height': block_height,
239
+ 'network_difficulty': network_difficulty if 'network_difficulty' in locals() else None
240
+ }
241
+ return template
242
+ try:
243
+ # Get latest block info
244
+ response = requests.get(self.bitcoin_network.latest_block_url)
245
+ if response.status_code != 200:
246
+ raise Exception("Failed to get latest block")
247
+
248
+ latest = response.json()
249
+ # Construct proper block template with real network data
250
+ template = {
251
+ 'version': 0x20000000, # Version 2 with BIP9 bits
252
+ 'previousblockhash': prev_block, # Changed to match Bitcoin Core naming
253
+ 'merkleroot': '0' * 64, # Will be calculated from transactions
254
+ 'time': int(time.time()), # Current time
255
+ 'bits': bits_int, # Using parsed bits value
256
+ 'height': height,
257
+ 'target': target,
258
+ 'difficulty': network_difficulty,
259
+ 'coinbasetx': coinbase_tx,
260
+ 'sizelimit': 4000000, # 4MB block size limit
261
+ 'transactions': [] # Pending transactions (empty for now)
262
+ }
263
+ return template
264
+ except Exception as e:
265
+ print(f"Error getting block template: {str(e)}")
266
+ # Create fallback template
267
+ template = {
268
+ 'version': 0x20000000, # Version 2 with BIP9 bits
269
+ 'previousblockhash': '000000000000000000024bead8df69990852c202db0e0097c1a12ea637d7e96d',
270
+ 'merkleroot': '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
271
+ 'time': int(time.time()),
272
+ 'bits': 0x1d00ffff,
273
+ 'target': 0x00000000ffff0000000000000000000000000000000000000000000000000000,
274
+ 'height': 2_500_000,
275
+ 'difficulty': 1.0,
276
+ 'coinbasetx': {
277
+ 'version': 1,
278
+ 'vin': [{
279
+ 'txid': '0' * 64,
280
+ 'vout': 0xFFFFFFFF,
281
+ 'scriptSig': '03' + hex(2_500_000)[2:].zfill(6) + '0000000000000000',
282
+ 'sequence': 0xFFFFFFFF
283
+ }],
284
+ 'vout': [{
285
+ 'value': 625000000,
286
+ 'scriptPubKey': '76a914' + hashlib.new("ripemd160", hashlib.sha256(self.wallet_address.encode()).digest()).hexdigest() + '88ac'
287
+ }]
288
+ },
289
+ 'sizelimit': 4000000,
290
+ 'transactions': []
291
+ }
292
+ return template
293
+
294
+ def submit_block(self, block_header: bytes, nonce: int) -> bool:
295
+ """Submit found block to network"""
296
+ try:
297
+ # Get current template
298
+ template = self.get_block_template()
299
+
300
+ # Create block data starting with header including nonce
301
+ block_data = bytearray(block_header[:-4] + struct.pack('<I', nonce))
302
+
303
+ # Add transaction count varint (1 for coinbase only)
304
+ block_data.extend(bytes([1]))
305
+
306
+ # Create proper coinbase transaction
307
+ block_height = template['height']
308
+ block_height_hex = hex(block_height)[2:].zfill(6) # BIP34: Block height
309
+
310
+ # Create proper coinbase script
311
+ coinbase_script = bytes.fromhex(
312
+ "03" + # Push 3 bytes
313
+ block_height_hex + # Block height (BIP34)
314
+ "0000000000000000" + # Extra nonce
315
+ "2f4d696e656420627920426974436f696e2d436f70696c6f742f" # /Mined by BitCoin-Copilot/
316
+ )
317
+
318
+ # Start serializing the coinbase transaction
319
+ tx_data = struct.pack('<I', 1) # Version 1
320
+
321
+ # Input count (always 1 for coinbase)
322
+ tx_data += bytes([1])
323
+
324
+ # Coinbase input with proper null txid (must be exactly 32 bytes)
325
+ tx_data += b'\x00' * 32 # Previous txid (null for coinbase)
326
+ tx_data += struct.pack('<I', 0xFFFFFFFF) # Previous output index (-1 for coinbase)
327
+
328
+ # Create proper coinbase input script with proper length prefix
329
+ script_len = len(coinbase_script)
330
+ if script_len < 0xfd:
331
+ tx_data += bytes([script_len])
332
+ elif script_len <= 0xffff:
333
+ tx_data += bytes([0xfd]) + struct.pack('<H', script_len)
334
+ elif script_len <= 0xffffffff:
335
+ tx_data += bytes([0xfe]) + struct.pack('<I', script_len)
336
+ else:
337
+ tx_data += bytes([0xff]) + struct.pack('<Q', script_len)
338
+
339
+ tx_data += coinbase_script # Coinbase script
340
+ tx_data += struct.pack('<I', 0xFFFFFFFF) # Sequence
341
+
342
+ # Output count (1 output paying the miner)
343
+ tx_data += bytes([1])
344
+
345
+ # Miner's reward output (6.25 BTC)
346
+ tx_data += struct.pack('<Q', 625000000) # Value in satoshis
347
+
348
+ # Create proper P2PKH script for payout
349
+ # First decode the base58 address to get the pubkey hash
350
+ from base58 import b58decode_check
351
+ decoded = b58decode_check(self.wallet_address)
352
+ pubkey_hash = decoded[1:] # Skip version byte
353
+
354
+ # Build P2PKH script
355
+ script_pubkey = bytes([0x76, 0xa9, 0x14]) + pubkey_hash + bytes([0x88, 0xac])
356
+ tx_data += bytes([len(script_pubkey)]) # Script length
357
+ tx_data += script_pubkey # P2PKH script
358
+
359
+ # Add locktime
360
+ tx_data += struct.pack('<I', 0) # nLockTime
361
+
362
+ # Add serialized coinbase transaction to block
363
+ block_data.extend(tx_data)
364
+
365
+ # Submit block using blockchain.info API
366
+ submit_url = 'https://api.blockchain.info/haskoin-store/btc/block'
367
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
368
+ response = requests.post(submit_url, data={'block': block_data.hex()}, headers=headers)
369
+
370
+ if response.status_code == 200:
371
+ print(f"Block successfully submitted!")
372
+ logging.info("Block submission successful")
373
+ return True
374
+ elif response.status_code == 400 and 'bad-txns-vin-empty' in response.text:
375
+ print("Block rejected: Invalid coinbase transaction structure")
376
+ logging.error("Block rejected due to invalid coinbase transaction")
377
+ return False
378
+ else:
379
+ error_msg = response.text if response.text else f"Status code: {response.status_code}"
380
+ print(f"Block submission failed: {error_msg}")
381
+ logging.error(f"Block submission failed: {error_msg}")
382
+ return False
383
+
384
+ except Exception as e:
385
+ print(f"Error submitting block: {str(e)}")
386
+ return False
387
+ try:
388
+ block_hash = hashlib.sha256(hashlib.sha256(block_header).digest()).digest()
389
+ return self.bitcoin_network.submit_block(block_header, nonce)
390
+ except Exception as e:
391
+ print(f"Block submission error: {e}")
392
+ return False
393
+
394
+ def _bits_to_target(self, bits: str) -> int:
395
+ """Convert compact bits to target"""
396
+ bits = int(bits, 16)
397
+ shift = (bits >> 24) & 0xff
398
+ target = (bits & 0x00ffffff) * (2 ** (8 * (shift - 3)))
399
  return target