Here just for you my friend improved version if you have the pubkey c-bit find it more quickly
#!/usr/bin/env python3
# coding: utf-8
"""
proof.py
Hash160 linear scan vs double c-bit on puzzle 21.
Uses only hash160 comparisons and no Base58 encoding. Added a progress bars tqdm.
"""
import hashlib, math
from ecdsa import SECP256k1, util
from multiprocessing import Pool, cpu_count
from tqdm import tqdm
# --- Configuration ---
ADDRESS_TARGET = "114oFNXucftsHiUMY8uctg6N487riuyXs4h"
HASH160_TARGET = "29a78213caa9eea824acf08022ab9dfc83414f56"
RANGE_HEX = "100000:1fffff"
FILTER_BITS = 2 # c ≥ 1 ⇒ >5% reduction
SHA_PREFILTER_BITS = 8 # double bits
THRESHOLD = 5.0 # percent
# -------------------------------
G = SECP256k1.generator
ORDER = SECP256k1.order
B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def b58decode(s: str) -> bytes:
num = 0
for ch in s:
num = num * 58 + B58.index(ch)
full = num.to_bytes((num.bit_length() + 7) // 8, 'big')
# leading-zero pad
pad = len(s) - len(s.lstrip('1'))
full = b'\x00'*pad + full
payload, chk = full[:-4], full[-4:]
if hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] != chk:
raise ValueError("Invalid Base58 checksum")
return payload
def hash160_pubkey(x: int) -> bytes:
P = x * G
prefix = b'\x02' if (P.y() & 1)==0 else b'\x03'
pub = prefix + util.number_to_string(P.x(), ORDER)
return hashlib.new('ripemd160', hashlib.sha256(pub).digest()).digest()
def get_target_h160() -> bytes:
if HASH160_TARGET:
return bytes.fromhex(HASH160_TARGET)
payload = b58decode(ADDRESS_TARGET)
return payload[1:]
def linear_scan(start: int, end: int, target_h: bytes):
ops = 0
for x in tqdm(range(start, end+1), desc="Linear scan", unit="key"):
ops += 1
if hash160_pubkey(x) == target_h:
return x, ops
return None, ops
def prefilter_chunk(args):
idx, start, end, c, d, t1, t2, target_h = args
# si d>0 et t2 défini, on fait le SHA256-prefilter
for i, x in enumerate(range(start, end+1), start=1):
# 1er filtre: SHA256(pub)
P = x * G
prefix = b'\x02' if (P.y() & 1)==0 else b'\x03'
pub = prefix + util.number_to_string(P.x(), ORDER)
sha = hashlib.sha256(pub).digest()
if d and t2 is not None:
if (int.from_bytes(sha, 'big') >> (256 - d)) != t2:
continue
# 2e filtre + comparaison finale: RIPEMD-160
rip = hashlib.new('ripemd160', sha).digest()
if (int.from_bytes(rip, 'big') >> (160 - c)) != t1:
continue
if rip == target_h:
return idx, i, x
return idx, None, None
def parallel_prefilter(start: int, end: int, c: int, d: int,
target_h: bytes, t2: int, workers: int):
N = end - start + 1
chunk = math.ceil(N / workers)
t1 = int.from_bytes(target_h, 'big') >> (160 - c)
args = []
for i in range(workers):
s = start + i*chunk
e = min(start + (i+1)*chunk - 1, end)
args.append((i, s, e, c, d, t1, t2, target_h))
with Pool(workers) as p:
# imap_unordered + barre de progression sur les chunks
for idx, ops, x in tqdm(p.imap_unordered(prefilter_chunk, args),
total=len(args),
desc="Prefilter chunks",
unit="chunk"):
if ops:
return x, ops
return None, 0
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--workers", type=int, default=0,
help="nombre de processus (défaut = CPU count)")
parser.add_argument("--pubkey", type=str, default=None,
help="hex compressed pubkey cible (pour SHA256-prefilter)")
args = parser.parse_args()
workers = args.workers or cpu_count()
s_hex, e_hex = RANGE_HEX.split(':')
start, end = int(s_hex, 16), int(e_hex, 16)
N = end - start + 1
print(f"Target address: {ADDRESS_TARGET}")
print(f"Range: 0x{s_hex} .. 0x{e_hex} (N = {N})")
print(f"Filter bits: {FILTER_BITS}, SHA256 bits: {SHA_PREFILTER_BITS}, "
f"Processes: {workers}\n")
target_h = get_target_h160()
# Calculate the SHA256-prefilter threshold if pubkey is provided
t2 = None
if args.pubkey:
pub_bytes = bytes.fromhex(args.pubkey)
sha_target = hashlib.sha256(pub_bytes).digest()
t2 = int.from_bytes(sha_target, 'big') >> (256 - SHA_PREFILTER_BITS)
# Linear scan
print("→ Linear hash160 scan…")
x_lin, ops_lin = linear_scan(start, end, target_h)
print(f" ✅ Found x = 0x{x_lin:x} in {ops_lin} H160 ops\n")
# Prefilter scan
print("→ Parallel double-prefilter scan…")
x_pre, ops_pre = parallel_prefilter(start, end,
FILTER_BITS,
SHA_PREFILTER_BITS,
target_h,
t2,
workers)
print(f" ✅ Found x = 0x{x_pre:x} in {ops_pre} heavy ops\n")
# Statistics
pct_lin = 100.0
pct_pre = ops_pre / ops_lin * 100.0 if ops_lin else 0.0
reduction = pct_lin - pct_pre
print(f"Percent checks: hash160 = {pct_lin:.2f}%, prefilter = {pct_pre:.2f}%")
print(f"Reduction = {reduction:.2f}%")
print(("✅" if reduction>THRESHOLD else "⚠️") +
f" Reduction {'exceeds' if reduction>THRESHOLD else 'below'} {THRESHOLD}%")
winner = "Prefilter" if ops_pre < ops_lin else "Hash160"
print("🏆 Winner: " + winner + " scan")
if __name__ == "__main__":
main()
root:~# python3 proof.py --pubkey 031a746c78f72754e0be046186df8a20cdce5c79b2eda76013c647af08d306e49e
Range: 0x100000 .. 0x1fffff (N = 1048576)
Linear scan: 73%|███████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 763188/1048576 [01:06<00:24, 11441.10key/s]
Prefilter chunks: 0%| | 0/4 [00:38<?, ?chunk/s]
My old computer doesn’t have the balls to scan the remaining keys, so I hope I’ve at least opened your eyes, you seem smarter than Ktimes ^^ Think of me if you use it! Your welcome my friend.