Fred808 commited on
Commit
9970769
·
verified ·
1 Parent(s): 512b661

Update network_integration.py

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