// Second block for length padding (only length=512 bits = 0x200 bits) uint data2[16] = 0; data2[0] = 0x00000080; // padding block data2[15] = 0x00000200; // bit length (512 bits)
// Check if solution (big-endian compare with target) // We just check first 32 bits (hash2[0] byteswapped to big-endian) uint be_hash0 = ((hash2[0] >> 24) & 0xFF) """ Helper: prepare block header (Bitcoin-style, 80 bytes) ------------------------------ def make_block_header(version, prev_hash, merkle_root, timestamp, bits, nonce=0): """Pack 80-byte Bitcoin block header (little-endian for each field except nonce)""" header = (pack("<I", version) + pack("<32s", bytes.fromhex(prev_hash)[::-1]) + pack("<32s", bytes.fromhex(merkle_root)[::-1]) + pack("<I", timestamp) + pack("<I", bits) + pack("<I", nonce)) return header
# Use first platform, select GPU device self.platform = platforms[0] self.device = None for dev in self.platform.get_devices(device_type=cl.device_type.GPU): self.device = dev break if not self.device: raise RuntimeError("No GPU device found") self.ctx = cl.Context([self.device]) self.queue = cl.CommandQueue(self.ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) # Build program self.program = cl.Program(self.ctx, KERNEL_CODE).build() print(f"Using GPU: self.device.name") print(f"Max work group size: self.device.max_work_group_size") def mine(self, block_header_hex, target_hex, start_nonce=0, nonce_range=2**24, work_size=None): """ block_header_hex: 80-byte hex string (without nonce, nonce will be zeroed) target_hex: target threshold as hex string (big-endian, e.g., '00000000ffff...') """ # Prepare fixed part (nonce = 0) header = bytes.fromhex(block_header_hex) if len(header) != 80: raise ValueError("Header must be 80 bytes") # Zero out the nonce field (last 4 bytes) to use as fixed base header = header[:76] + b'\x00\x00\x00\x00' fixed_words = header_to_words(header) # Target (first 32 bits big-endian) target_bytes = bytes.fromhex(target_hex) target_high = unpack(">I", target_bytes[:4])[0] # GPU buffers fixed_buf = cl.Buffer(self.ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=np.array(fixed_words, dtype=np.uint32)) results_buf = cl.Buffer(self.ctx, cl.mem_flags.WRITE_ONLY, 3 * 4) # 3 uint32 # Kernel arguments self.program.mine.set_args(fixed_buf, results_buf, np.uint32(start_nonce), np.uint32(target_high)) # Work size if work_size is None: work_size = min(nonce_range, self.device.max_work_group_size * 32) global_size = (work_size,) print(f"Mining from nonce start_nonce to start_nonce + nonce_range - 1") print(f"Target (first 32 bits): 0xtarget_high:08x") print(f"Work size: work_size\n") start_time = time.time() hashes = 0 for offset in range(0, nonce_range, work_size): current_start = start_nonce + offset self.program.mine.set_args(fixed_buf, results_buf, np.uint32(current_start), np.uint32(target_high)) # Execute kernel event = self.program.mine(self.queue, global_size, None) event.wait() # Read results results = np.zeros(3, dtype=np.uint32) cl.enqueue_copy(self.queue, results, results_buf).wait() hashes += work_size if results[0] != 0: # Solution found elapsed = time.time() - start_time speed = hashes / elapsed / 1e6 print(f"\n✅ SOLUTION FOUND after hashes hashes (speed:.2f MH/s)") print(f"Nonce: results[0] (0xresults[0]:08x)") print(f"Hash (first 64 bits): results[1]:08xresults[2]:08x") # Recompute final header final_header = header[:76] + pack("<I", results[0]) final_hash = hashlib.sha256(hashlib.sha256(final_header).digest()).digest() print(f"Full hash (big-endian): final_hash.hex()") return results[0] # Progress report if (offset // work_size) % 256 == 0 and offset > 0: elapsed = time.time() - start_time speed = hashes / elapsed / 1e6 print(f" ... nonce current_start:12d | speed:.2f MH/s") print("\n❌ No solution found in given nonce range") return None Example usage ------------------------------ if name == " main ": # Example: Bitcoin block #0 (genesis) - very easy target # Genesis block header (80 bytes, nonce originally 2083236893) # We'll mine a dummy header with low difficulty sha256 gpu miner
// Copy fixed block words (0..13) and add nonce at word 14 uint data[16]; for (int i = 0; i < 14; i++) data[i] = fixed_block[i];
hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; hash[4] += e; hash[5] += f; hash[6] += g; hash[7] += h; // Second block for length padding (only length=512
# Target: first 4 bytes must be <= 0x0000ffff (very easy) # For difficulty 1, target = 0x00000000ffff000000... easy_target = "0000ffff" + "00"*28
void sha256_transform(const uint *data, uint *hash) uint W[64]; uint i, t1, t2; uint a, b, c, d, e, f, g, h; # For quick demo, we use a very
# Create a dummy block header (80 bytes hex) with version, prev hash, merkle root, time, bits # Bits = 0x1d00ffff -> target ~ 0x00000000ffff000000... # For quick demo, we use a very high target (easy)
dummy_header = ( "01000000" + # version "0000000000000000000000000000000000000000000000000000000000000000" + # prev hash "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" + # merkle root (coinbase) "29ab5f49" + # timestamp (2017-ish) "ffff001d" + # bits (very low difficulty) "00000000" # nonce (zero) )
miner = SHA256GPUMiner()
data[14] = nonce; data[15] = 0x80000000; // padding (bit 1, then zeros)