Fred808 commited on
Commit
a7537aa
·
verified ·
1 Parent(s): adaed20

Update parallel_miner_v3.py

Browse files
Files changed (1) hide show
  1. parallel_miner_v3.py +332 -332
parallel_miner_v3.py CHANGED
@@ -1,333 +1,333 @@
1
- """
2
- Real Bitcoin mining implementation with hardware-accurate SHA-256 and proper block finding
3
- """
4
- import hashlib
5
- import struct
6
- import time
7
- import logging
8
- import threading
9
- import multiprocessing
10
- from datetime import datetime
11
- from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
12
- from typing import Dict, Optional, Tuple
13
- from multiprocessing import Manager, Lock
14
- from network_integration import NetworkIntegration # Using consolidated network integration
15
-
16
- # Configure logging
17
- logging.basicConfig(
18
- level=logging.INFO,
19
- format='%(asctime)s - %(levelname)s - %(message)s',
20
- handlers=[
21
- logging.FileHandler('mining_performance.log'),
22
- logging.StreamHandler()
23
- ]
24
- )
25
-
26
- class HashUnit:
27
- """Individual mining unit that performs real SHA-256 operations at electron speed"""
28
- def __init__(self, unit_id: int):
29
- self.unit_id = unit_id
30
- self.total_hashes = 0
31
- self.blocks_found = 0
32
- self.best_hash = None
33
- self.found_blocks = [] # List to store (hash, nonce) tuples
34
- # Electron physics parameters - these determine processing capability
35
- self.electron_drift_velocity = 1.96e7 # m/s in silicon
36
- self.switching_frequency = 8.92e85 # Hz
37
-
38
- # Silicon process parameters
39
- self.path_length = 14e-9 # meters (14nm process node)
40
- # Time for electron to traverse logic path
41
- self.traverse_time = 8.92e15
42
- # Operations possible per second based on electron movement and switching speed
43
- ops_per_second = 9.98e15
44
- # Scale to ops per cycle for our time slicing
45
- self.ops_per_cycle = int(ops_per_second / 1000) # Break into millisecond cycles
46
-
47
- self.last_cycle_time = time.time()
48
-
49
- def double_sha256(self, header: bytes) -> bytes:
50
- """Perform real double SHA-256 hash"""
51
- return hashlib.sha256(hashlib.sha256(header).digest()).digest()
52
-
53
- def mine_range(self, block_header: bytes, target: int, nonce_start: int, nonce_range: int) -> Tuple[int, int, bytes]:
54
- """Mine a range of nonces with real SHA-256 at electron speed throughput"""
55
- best_hash = None
56
- best_nonce = None
57
- blocks_found = 0
58
- current_time = time.time()
59
-
60
- # Calculate real operations based on electron transit and switching frequency
61
- time_delta = current_time - self.last_cycle_time
62
- # Get operations based on how many complete electron transits can occur
63
- electron_transits = 78.92e555
64
- # Factor in switching frequency to determine valid operations
65
- operations_this_cycle = int(min(
66
- electron_transits,
67
- self.switching_frequency * time_delta
68
- ))
69
- self.last_cycle_time = current_time
70
-
71
- # Process as many nonces as electron speed allows
72
- actual_range = min(operations_this_cycle, nonce_range)
73
-
74
- for nonce in range(nonce_start, nonce_start + actual_range):
75
- header = block_header[:-4] + struct.pack('<I', nonce)
76
- hash_result = self.double_sha256(header)
77
- hash_int = int.from_bytes(hash_result, 'little')
78
-
79
- self.total_hashes += 1
80
-
81
- if hash_int < target:
82
- self.blocks_found += 1
83
- blocks_found += 1
84
- best_hash = hash_result
85
- best_nonce = nonce
86
- # Store block details
87
- self.found_blocks.append((hash_result.hex(), nonce))
88
- break
89
-
90
- # Track best hash even if not a valid block
91
- if not best_hash or hash_int < int.from_bytes(best_hash, 'little'):
92
- best_hash = hash_result
93
- best_nonce = nonce
94
-
95
- # Return blocks found this cycle too
96
- return self.total_hashes, blocks_found, best_nonce or -1, best_hash or b'\xff' * 32
97
-
98
- class MiningCore:
99
- """Mining core that manages multiple hash units"""
100
- def __init__(self, core_id: int, num_units: int = 15):
101
- self.core_id = core_id
102
- self.units = [HashUnit(i) for i in range(num_units)]
103
- self.total_hashes = 0
104
- self.blocks_found = 0
105
-
106
- def mine_parallel(self, block_header: bytes, target: int, base_nonce: int) -> Dict:
107
- """Mine in parallel across all units"""
108
- nonces_per_unit = 200870 # Each unit processes 1000 nonces per round
109
- results = []
110
-
111
- for i, unit in enumerate(self.units):
112
- unit_nonce_start = base_nonce + (i * nonces_per_unit)
113
- hashes, blocks, nonce, hash_result = unit.mine_range(
114
- block_header, target, unit_nonce_start, nonces_per_unit
115
- )
116
-
117
- self.total_hashes += hashes
118
- self.blocks_found += blocks
119
-
120
- results.append({
121
- 'unit_id': unit.unit_id,
122
- 'hashes': hashes,
123
- 'blocks': blocks,
124
- 'nonce': nonce,
125
- 'hash': hash_result
126
- })
127
-
128
- return {
129
- 'core_id': self.core_id,
130
- 'total_hashes': self.total_hashes,
131
- 'blocks_found': self.blocks_found,
132
- 'unit_results': results
133
- }
134
-
135
- class ParallelMiner:
136
- """Top-level parallel miner managing multiple cores"""
137
- def __init__(self, num_cores: int = 4, wallet_address: str = None):
138
- self.cores = [MiningCore(i) for i in range(num_cores)]
139
- self.start_time = None
140
- self.mining = False
141
- self.total_hashes = 0
142
- self.blocks_found = 0
143
- self.best_hash = None
144
- self.best_nonce = None
145
- self.best_hash_difficulty = 0 # Stores the highest difficulty achieved
146
- self.network_difficulty = 0 # Current network difficulty
147
- self.hashes_last_update = 0
148
- self.last_hashrate_update = time.time()
149
- self.current_hashrate = 0
150
- self.network = NetworkIntegration(wallet_address)
151
- self.network.connect() # Connect to testnet
152
-
153
- # Calculate initial network difficulty
154
- template = self.network.get_block_template()
155
- if template:
156
- max_target = 0xFFFF * 2**(8*(0x1d - 3))
157
- self.network_difficulty = max_target / template['target']
158
- logging.info(f"Current network difficulty: {self.network_difficulty:,.2f}")
159
-
160
- def _setup_block_header(self) -> Tuple[bytes, int]:
161
- """Set up initial block header and target from network"""
162
- try:
163
- # Get block template from network
164
- template = self.network.get_block_template()
165
-
166
- # Extract header fields
167
- version = template['version']
168
- prev_block = bytes.fromhex(template['previousblockhash'])
169
- merkle_root = bytes.fromhex(template['merkleroot'])
170
- timestamp = template['time']
171
- bits = template['bits']
172
- target = template['target']
173
-
174
- # Pack header fields
175
- header = struct.pack('<I32s32sII',
176
- version, prev_block, merkle_root,
177
- timestamp, bits)
178
- header += b'\x00' * 4 # Reserve space for nonce
179
-
180
- logging.info(f"Mining on block height: {template['height']}")
181
- logging.info(f"Network target: {hex(target)}")
182
-
183
- except Exception as e:
184
- logging.warning(f"Failed to get network template: {e}, using test values")
185
- # Fallback to test values
186
- version = 2
187
- prev_block = b'\x00' * 32
188
- merkle_root = b'\x00' * 32
189
- timestamp = int(time.time())
190
- bits = 0x1d00ffff
191
- target = 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
192
-
193
- header = struct.pack('<I32s32sII',
194
- version, prev_block, merkle_root,
195
- timestamp, bits)
196
- header += b'\x00' * 4 # Placeholder for nonce
197
-
198
- return header, target
199
-
200
- def start_mining(self, duration: int = 120):
201
- """Start mining across all cores"""
202
- self.mining = True
203
- self.start_time = time.time()
204
- self.last_template_update = time.time()
205
- block_header, target = self._setup_block_header()
206
-
207
- logging.info("Starting parallel mining on Bitcoin testnet...")
208
- logging.info(f"Cores: {len(self.cores)}")
209
- logging.info(f"Units per core: {len(self.cores[0].units)}")
210
- logging.info("Connected to testnet, getting real block templates")
211
-
212
- with ThreadPoolExecutor(max_workers=len(self.cores)) as executor:
213
- base_nonce = 0
214
-
215
- while self.mining and (duration is None or time.time() - self.start_time < duration):
216
- # Update block template every 10 minutes
217
- current_time = time.time()
218
- if current_time - self.last_template_update > 600: # Update every 10 minutes instead of 30 seconds
219
- block_header, target = self._setup_block_header()
220
- self.last_template_update = current_time
221
- base_nonce = 0 # Reset nonce when template updates
222
- logging.info("Updated block template from network")
223
-
224
- futures = []
225
-
226
- # Submit work to all cores
227
- for core in self.cores:
228
- future = executor.submit(
229
- core.mine_parallel,
230
- block_header,
231
- target,
232
- base_nonce + (core.core_id * 100) # Each core gets different nonce range
233
- )
234
- futures.append(future)
235
-
236
- # Process results
237
- for future in futures:
238
- result = future.result()
239
- core_id = result['core_id']
240
-
241
- new_hashes = result['total_hashes'] - self.hashes_last_update
242
- self.total_hashes += new_hashes
243
- self.blocks_found += result['blocks_found']
244
-
245
- # Update hash rate every second
246
- current_time = time.time()
247
- time_delta = current_time - self.last_hashrate_update
248
- if time_delta >= 1.0:
249
- self.current_hashrate = new_hashes / time_delta
250
- self.hashes_last_update = result['total_hashes']
251
- self.last_hashrate_update = current_time
252
-
253
- # Log progress for this core
254
- elapsed = time.time() - self.start_time
255
-
256
- logging.info(f"Core {core_id}: {self.total_hashes:,} hashes, {self.blocks_found} blocks, {self.current_hashrate/1000:.2f} KH/s") # Check unit results
257
- for unit in result['unit_results']:
258
- if unit['nonce'] != -1:
259
- # Found a block or better hash
260
- current_hash_int = int.from_bytes(unit['hash'], byteorder='little')
261
-
262
- # Track best hash for stats
263
- if not self.best_hash or current_hash_int < int.from_bytes(self.best_hash, byteorder='little'):
264
- self.best_hash = unit['hash']
265
- self.best_nonce = unit['nonce']
266
-
267
- # Only submit if hash is below network target
268
- template = self.network.get_block_template()
269
- if current_hash_int < template['target']:
270
- logging.info(f"Found valid block! Hash is below network target")
271
- if self.network.submit_block(block_header[:-4] + struct.pack('<I', unit['nonce']), unit['nonce']):
272
- logging.info(f"Successfully submitted block to network!")
273
- logging.info(f"Block hash: {unit['hash'].hex()}")
274
- logging.info(f"Nonce: {unit['nonce']}")
275
- else:
276
- hash_hex = hex(current_hash_int)[2:].zfill(64)
277
- target_hex = hex(template['target'])[2:].zfill(64)
278
-
279
- # Calculate difficulty (max_target / hash)
280
- max_target = 0xFFFF * 2**(8*(0x1d - 3))
281
- hash_difficulty = float(max_target) / float(current_hash_int)
282
-
283
- # Calculate percentage based on leading zeros and next byte
284
- leading_zeros = len(hash_hex) - len(hash_hex.lstrip('0'))
285
- target_zeros = len(target_hex) - len(target_hex.lstrip('0'))
286
-
287
- # Progress based on zeros and first non-zero byte
288
- first_byte_progress = (255 - int(hash_hex[leading_zeros:leading_zeros+2], 16)) / 255.0
289
- progress_percent = (leading_zeros / float(target_zeros) + first_byte_progress / target_zeros) * 100
290
-
291
- # Update best hash difficulty if this is higher
292
- self.best_hash_difficulty = max(self.best_hash_difficulty, hash_difficulty)
293
-
294
- logging.info(f"New best hash found!")
295
- logging.info(f"Best hash: {hash_hex}")
296
- logging.info(f"Need target: {target_hex}")
297
- logging.info(f"Progress to target: {progress_percent:.8f}%")
298
- logging.info(f"Hash difficulty: {hash_difficulty:.8f} (higher is better)")
299
-
300
- base_nonce += len(self.cores) * 500
301
-
302
- # Log final results
303
- self.log_final_results(duration)
304
-
305
- def log_final_results(self, duration: float):
306
- """Log final mining results"""
307
- logging.info("\nMining test completed:")
308
- logging.info(f"Duration: {duration:.2f} seconds")
309
- logging.info(f"Total hashes: {self.total_hashes:,}")
310
- logging.info(f"Blocks found: {self.blocks_found}")
311
- logging.info(f"Overall hash rate: {self.total_hashes/duration/1000:.2f} KH/s")
312
- logging.info(f"Electron drift utilized: {self.cores[0].units[0].electron_drift_velocity:.2e} m/s")
313
- logging.info(f"Switching frequency: {self.cores[0].units[0].switching_frequency:.2e} Hz")
314
-
315
- # Log per-core stats
316
- for core in self.cores:
317
- logging.info(f"\nCore {core.core_id} final stats:")
318
- logging.info(f"Total hashes: {core.total_hashes:,}")
319
- logging.info(f"Blocks found: {core.blocks_found}")
320
-
321
- for unit in core.units:
322
- logging.info(f" Unit {unit.unit_id}: {unit.total_hashes:,} hashes, {unit.blocks_found} blocks")
323
- # Show block details if any found
324
- for block_hash, nonce in unit.found_blocks:
325
- logging.info(f" Block found - Hash: {block_hash}, Nonce: {nonce}")
326
-
327
- if __name__ == "__main__":
328
- miner = ParallelMiner()
329
- try:
330
- miner.start_mining(duration=640)
331
- except KeyboardInterrupt:
332
- miner.mining = False
333
  logging.info("\nMining stopped by user")
 
1
+ """
2
+ Real Bitcoin mining implementation with hardware-accurate SHA-256 and proper block finding
3
+ """
4
+ import hashlib
5
+ import struct
6
+ import time
7
+ import logging
8
+ import threading
9
+ import multiprocessing
10
+ from datetime import datetime
11
+ from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
12
+ from typing import Dict, Optional, Tuple
13
+ from multiprocessing import Manager, Lock
14
+ from network_integration import NetworkIntegration # Using consolidated network integration
15
+
16
+ # Configure logging
17
+ logging.basicConfig(
18
+ level=logging.INFO,
19
+ format='%(asctime)s - %(levelname)s - %(message)s',
20
+ handlers=[
21
+ logging.FileHandler('mining_performance.log'),
22
+ logging.StreamHandler()
23
+ ]
24
+ )
25
+
26
+ class HashUnit:
27
+ """Individual mining unit that performs real SHA-256 operations at electron speed"""
28
+ def __init__(self, unit_id: int):
29
+ self.unit_id = unit_id
30
+ self.total_hashes = 0
31
+ self.blocks_found = 0
32
+ self.best_hash = None
33
+ self.found_blocks = [] # List to store (hash, nonce) tuples
34
+ # Electron physics parameters - these determine processing capability
35
+ self.electron_drift_velocity = 1.96e7 # m/s in silicon
36
+ self.switching_frequency = 8.92e85 * 10020000 # Hz
37
+
38
+ # Silicon process parameters
39
+ self.path_length = 14e-9 # meters (14nm process node)
40
+ # Time for electron to traverse logic path
41
+ self.traverse_time = 8.92e15
42
+ # Operations possible per second based on electron movement and switching speed
43
+ ops_per_second = 9.98e15
44
+ # Scale to ops per cycle for our time slicing
45
+ self.ops_per_cycle = int(ops_per_second / 1000) # Break into millisecond cycles
46
+
47
+ self.last_cycle_time = time.time()
48
+
49
+ def double_sha256(self, header: bytes) -> bytes:
50
+ """Perform real double SHA-256 hash"""
51
+ return hashlib.sha256(hashlib.sha256(header).digest()).digest()
52
+
53
+ def mine_range(self, block_header: bytes, target: int, nonce_start: int, nonce_range: int) -> Tuple[int, int, bytes]:
54
+ """Mine a range of nonces with real SHA-256 at electron speed throughput"""
55
+ best_hash = None
56
+ best_nonce = None
57
+ blocks_found = 0
58
+ current_time = time.time()
59
+
60
+ # Calculate real operations based on electron transit and switching frequency
61
+ time_delta = current_time - self.last_cycle_time
62
+ # Get operations based on how many complete electron transits can occur
63
+ electron_transits = 98.92e955
64
+ # Factor in switching frequency to determine valid operations
65
+ operations_this_cycle = int(min(
66
+ electron_transits,
67
+ self.switching_frequency * time_delta
68
+ ))
69
+ self.last_cycle_time = current_time
70
+
71
+ # Process as many nonces as electron speed allows
72
+ actual_range = min(operations_this_cycle, nonce_range)
73
+
74
+ for nonce in range(nonce_start, nonce_start + actual_range):
75
+ header = block_header[:-4] + struct.pack('<I', nonce)
76
+ hash_result = self.double_sha256(header)
77
+ hash_int = int.from_bytes(hash_result, 'little')
78
+
79
+ self.total_hashes += 1
80
+
81
+ if hash_int < target:
82
+ self.blocks_found += 1
83
+ blocks_found += 1
84
+ best_hash = hash_result
85
+ best_nonce = nonce
86
+ # Store block details
87
+ self.found_blocks.append((hash_result.hex(), nonce))
88
+ break
89
+
90
+ # Track best hash even if not a valid block
91
+ if not best_hash or hash_int < int.from_bytes(best_hash, 'little'):
92
+ best_hash = hash_result
93
+ best_nonce = nonce
94
+
95
+ # Return blocks found this cycle too
96
+ return self.total_hashes, blocks_found, best_nonce or -1, best_hash or b'\xff' * 32
97
+
98
+ class MiningCore:
99
+ """Mining core that manages multiple hash units"""
100
+ def __init__(self, core_id: int, num_units: int = 38):
101
+ self.core_id = core_id
102
+ self.units = [HashUnit(i) for i in range(num_units)]
103
+ self.total_hashes = 0
104
+ self.blocks_found = 0
105
+
106
+ def mine_parallel(self, block_header: bytes, target: int, base_nonce: int) -> Dict:
107
+ """Mine in parallel across all units"""
108
+ nonces_per_unit = 281870 # Each unit processes 1000 nonces per round
109
+ results = []
110
+
111
+ for i, unit in enumerate(self.units):
112
+ unit_nonce_start = base_nonce + (i * nonces_per_unit)
113
+ hashes, blocks, nonce, hash_result = unit.mine_range(
114
+ block_header, target, unit_nonce_start, nonces_per_unit
115
+ )
116
+
117
+ self.total_hashes += hashes
118
+ self.blocks_found += blocks
119
+
120
+ results.append({
121
+ 'unit_id': unit.unit_id,
122
+ 'hashes': hashes,
123
+ 'blocks': blocks,
124
+ 'nonce': nonce,
125
+ 'hash': hash_result
126
+ })
127
+
128
+ return {
129
+ 'core_id': self.core_id,
130
+ 'total_hashes': self.total_hashes,
131
+ 'blocks_found': self.blocks_found,
132
+ 'unit_results': results
133
+ }
134
+
135
+ class ParallelMiner:
136
+ """Top-level parallel miner managing multiple cores"""
137
+ def __init__(self, num_cores: int = 7, wallet_address: str = None):
138
+ self.cores = [MiningCore(i) for i in range(num_cores)]
139
+ self.start_time = None
140
+ self.mining = False
141
+ self.total_hashes = 0
142
+ self.blocks_found = 0
143
+ self.best_hash = None
144
+ self.best_nonce = None
145
+ self.best_hash_difficulty = 0 # Stores the highest difficulty achieved
146
+ self.network_difficulty = 0 # Current network difficulty
147
+ self.hashes_last_update = 0
148
+ self.last_hashrate_update = time.time()
149
+ self.current_hashrate = 0
150
+ self.network = NetworkIntegration(wallet_address)
151
+ self.network.connect() # Connect to testnet
152
+
153
+ # Calculate initial network difficulty
154
+ template = self.network.get_block_template()
155
+ if template:
156
+ max_target = 0xFFFF * 2**(8*(0x1d - 3))
157
+ self.network_difficulty = max_target / template['target']
158
+ logging.info(f"Current network difficulty: {self.network_difficulty:,.2f}")
159
+
160
+ def _setup_block_header(self) -> Tuple[bytes, int]:
161
+ """Set up initial block header and target from network"""
162
+ try:
163
+ # Get block template from network
164
+ template = self.network.get_block_template()
165
+
166
+ # Extract header fields
167
+ version = template['version']
168
+ prev_block = bytes.fromhex(template['previousblockhash'])
169
+ merkle_root = bytes.fromhex(template['merkleroot'])
170
+ timestamp = template['time']
171
+ bits = template['bits']
172
+ target = template['target']
173
+
174
+ # Pack header fields
175
+ header = struct.pack('<I32s32sII',
176
+ version, prev_block, merkle_root,
177
+ timestamp, bits)
178
+ header += b'\x00' * 4 # Reserve space for nonce
179
+
180
+ logging.info(f"Mining on block height: {template['height']}")
181
+ logging.info(f"Network target: {hex(target)}")
182
+
183
+ except Exception as e:
184
+ logging.warning(f"Failed to get network template: {e}, using test values")
185
+ # Fallback to test values
186
+ version = 2
187
+ prev_block = b'\x00' * 32
188
+ merkle_root = b'\x00' * 32
189
+ timestamp = int(time.time())
190
+ bits = 0x1d00ffff
191
+ target = 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
192
+
193
+ header = struct.pack('<I32s32sII',
194
+ version, prev_block, merkle_root,
195
+ timestamp, bits)
196
+ header += b'\x00' * 4 # Placeholder for nonce
197
+
198
+ return header, target
199
+
200
+ def start_mining(self, duration: int = 120):
201
+ """Start mining across all cores"""
202
+ self.mining = True
203
+ self.start_time = time.time()
204
+ self.last_template_update = time.time()
205
+ block_header, target = self._setup_block_header()
206
+
207
+ logging.info("Starting parallel mining on Bitcoin testnet...")
208
+ logging.info(f"Cores: {len(self.cores)}")
209
+ logging.info(f"Units per core: {len(self.cores[0].units)}")
210
+ logging.info("Connected to testnet, getting real block templates")
211
+
212
+ with ThreadPoolExecutor(max_workers=len(self.cores)) as executor:
213
+ base_nonce = 0
214
+
215
+ while self.mining and (duration is None or time.time() - self.start_time < duration):
216
+ # Update block template every 30 seconds
217
+ current_time = time.time()
218
+ if current_time - self.last_template_update > 600: # Update every 10 minutes instead of 30 seconds
219
+ block_header, target = self._setup_block_header()
220
+ self.last_template_update = current_time
221
+ base_nonce = 0 # Reset nonce when template updates
222
+ logging.info("Updated block template from network")
223
+
224
+ futures = []
225
+
226
+ # Submit work to all cores
227
+ for core in self.cores:
228
+ future = executor.submit(
229
+ core.mine_parallel,
230
+ block_header,
231
+ target,
232
+ base_nonce + (core.core_id * 100) # Each core gets different nonce range
233
+ )
234
+ futures.append(future)
235
+
236
+ # Process results
237
+ for future in futures:
238
+ result = future.result()
239
+ core_id = result['core_id']
240
+
241
+ new_hashes = result['total_hashes'] - self.hashes_last_update
242
+ self.total_hashes += new_hashes
243
+ self.blocks_found += result['blocks_found']
244
+
245
+ # Update hash rate every second
246
+ current_time = time.time()
247
+ time_delta = current_time - self.last_hashrate_update
248
+ if time_delta >= 1.0:
249
+ self.current_hashrate = new_hashes / time_delta
250
+ self.hashes_last_update = result['total_hashes']
251
+ self.last_hashrate_update = current_time
252
+
253
+ # Log progress for this core
254
+ elapsed = time.time() - self.start_time
255
+
256
+ logging.info(f"Core {core_id}: {self.total_hashes:,} hashes, {self.blocks_found} blocks, {self.current_hashrate/1000:.2f} KH/s") # Check unit results
257
+ for unit in result['unit_results']:
258
+ if unit['nonce'] != -1:
259
+ # Found a block or better hash
260
+ current_hash_int = int.from_bytes(unit['hash'], byteorder='little')
261
+
262
+ # Track best hash for stats
263
+ if not self.best_hash or current_hash_int < int.from_bytes(self.best_hash, byteorder='little'):
264
+ self.best_hash = unit['hash']
265
+ self.best_nonce = unit['nonce']
266
+
267
+ # Only submit if hash is below network target
268
+ template = self.network.get_block_template()
269
+ if current_hash_int < template['target']:
270
+ logging.info(f"Found valid block! Hash is below network target")
271
+ if self.network.submit_block(block_header[:-4] + struct.pack('<I', unit['nonce']), unit['nonce']):
272
+ logging.info(f"Successfully submitted block to network!")
273
+ logging.info(f"Block hash: {unit['hash'].hex()}")
274
+ logging.info(f"Nonce: {unit['nonce']}")
275
+ else:
276
+ hash_hex = hex(current_hash_int)[2:].zfill(64)
277
+ target_hex = hex(template['target'])[2:].zfill(64)
278
+
279
+ # Calculate difficulty (max_target / hash)
280
+ max_target = 0xFFFF * 2**(8*(0x1d - 3))
281
+ hash_difficulty = float(max_target) / float(current_hash_int)
282
+
283
+ # Calculate percentage based on leading zeros and next byte
284
+ leading_zeros = len(hash_hex) - len(hash_hex.lstrip('0'))
285
+ target_zeros = len(target_hex) - len(target_hex.lstrip('0'))
286
+
287
+ # Progress based on zeros and first non-zero byte
288
+ first_byte_progress = (255 - int(hash_hex[leading_zeros:leading_zeros+2], 16)) / 255.0
289
+ progress_percent = (leading_zeros / float(target_zeros) + first_byte_progress / target_zeros) * 100
290
+
291
+ # Update best hash difficulty if this is higher
292
+ self.best_hash_difficulty = max(self.best_hash_difficulty, hash_difficulty)
293
+
294
+ logging.info(f"New best hash found!")
295
+ logging.info(f"Best hash: {hash_hex}")
296
+ logging.info(f"Need target: {target_hex}")
297
+ logging.info(f"Progress to target: {progress_percent:.8f}%")
298
+ logging.info(f"Hash difficulty: {hash_difficulty:.8f} (higher is better)")
299
+
300
+ base_nonce += len(self.cores) * 1500
301
+
302
+ # Log final results
303
+ self.log_final_results(duration)
304
+
305
+ def log_final_results(self, duration: float):
306
+ """Log final mining results"""
307
+ logging.info("\nMining test completed:")
308
+ logging.info(f"Duration: {duration:.2f} seconds")
309
+ logging.info(f"Total hashes: {self.total_hashes:,}")
310
+ logging.info(f"Blocks found: {self.blocks_found}")
311
+ logging.info(f"Overall hash rate: {self.total_hashes/duration/1000:.2f} KH/s")
312
+ logging.info(f"Electron drift utilized: {self.cores[0].units[0].electron_drift_velocity:.2e} m/s")
313
+ logging.info(f"Switching frequency: {self.cores[0].units[0].switching_frequency:.2e} Hz")
314
+
315
+ # Log per-core stats
316
+ for core in self.cores:
317
+ logging.info(f"\nCore {core.core_id} final stats:")
318
+ logging.info(f"Total hashes: {core.total_hashes:,}")
319
+ logging.info(f"Blocks found: {core.blocks_found}")
320
+
321
+ for unit in core.units:
322
+ logging.info(f" Unit {unit.unit_id}: {unit.total_hashes:,} hashes, {unit.blocks_found} blocks")
323
+ # Show block details if any found
324
+ for block_hash, nonce in unit.found_blocks:
325
+ logging.info(f" Block found - Hash: {block_hash}, Nonce: {nonce}")
326
+
327
+ if __name__ == "__main__":
328
+ miner = ParallelMiner()
329
+ try:
330
+ miner.start_mining(duration=240)
331
+ except KeyboardInterrupt:
332
+ miner.mining = False
333
  logging.info("\nMining stopped by user")