Author

Topic: Bitcoin puzzle transaction ~32 BTC prize to who solves it - page 182. (Read 244669 times)

member
Activity: 503
Merit: 38

Script above is the python version of your code written in alien language, I'm on phone so I couldn't test to see if it works, later I will test it on my laptop and fix any issues. Insha'Allah. ( God willing )

Also;
Thanks for the update on the code, appreciate it. My scripts ( small part of them ) don't need much speed because they are not supposed to auto solve a key, they are intended as learning tools, I talked about improving performance to make king of information stop whining so much. 🤣

I use GMP even for random number generation..I'm playing around with collisions and cycles now.  Grin

Code:
import sys
import os
import time
import gmpy2
from gmpy2 import mpz
from functools import lru_cache
import secp256k1 as ice
import multiprocessing
from multiprocessing import Pool, cpu_count

# Constants
MODULO = gmpy2.mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
ORDER = gmpy2.mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141)
GX = gmpy2.mpz(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
GY = gmpy2.mpz(0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)

# Define Point class
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

PG = Point(GX, GY)
ZERO_POINT = Point(0, 0)

# Function to multiply a point by 2
@lru_cache(maxsize=None)
def multiply_by_2(P, p=MODULO):
    c = gmpy2.f_mod(3 * P.x * P.x * gmpy2.powmod(2 * P.y, -1, p), p)
    R = Point()
    R.x = gmpy2.f_mod(c * c - 2 * P.x, p)
    R.y = gmpy2.f_mod(c * (P.x - R.x) - P.y, p)
    return R

# Function to add two points
@lru_cache(maxsize=None)
def add_points(P, Q, p=MODULO):
    dx = Q.x - P.x
    dy = Q.y - P.y
    c = gmpy2.f_mod(dy * gmpy2.invert(dx, p), p)
    R = Point()
    R.x = gmpy2.f_mod(c * c - P.x - Q.x, p)
    R.y = gmpy2.f_mod(c * (P.x - R.x) - P.y, p)
    return R

# Function to calculate Y-coordinate from X-coordinate
@lru_cache(maxsize=None)
def x_to_y(X, y_parity, p=MODULO):
    Y = gmpy2.mpz(3)
    tmp = gmpy2.mpz(1)

    while Y > 0:
        if Y % 2 == 1:
            tmp = gmpy2.f_mod(tmp * X, p)
        Y >>= 1
        X = gmpy2.f_mod(X * X, p)

    X = gmpy2.f_mod(tmp + 7, p)

    Y = gmpy2.f_div(gmpy2.add(p, 1), 4)
    tmp = gmpy2.mpz(1)

    while Y > 0:
        if Y % 2 == 1:
            tmp = gmpy2.f_mod(tmp * X, p)
        Y >>= 1
        X = gmpy2.f_mod(X * X, p)

    Y = tmp

    if Y % 2 != y_parity:
        Y = gmpy2.f_mod(-Y, p)

    return Y

# Function to compute a table of points
def compute_point_table():
    points = [PG]
    for k in range(255):
        points.append(multiply_by_2(points[k]))
    return points

POINTS_TABLE = compute_point_table()

# Global event to signal all processes to stop
STOP_EVENT = multiprocessing.Event()

# Function to check and compare points for potential solutions
def check(P, Pindex, DP_rarity, A, Ak, B, Bk):
    check_modulo = gmpy2.f_mod(P.x, DP_rarity)
   
    if check_modulo != 0:
        return False

    message = f"\r[+] [Pindex]: {mpz(Pindex)}"
    messages = []
    messages.append(message)
    output = "\033[01;33m" + ''.join(messages) + "\r"
    sys.stdout.write(output)
    sys.stdout.flush()

    A.append(mpz(P.x))
    Ak.append(mpz(Pindex))

    collision_index_A = find_collision(Ak)
    if collision_index_A is not None:
        return comparator(A, Ak, B, Bk, collision_index_A)

    collision_index_B = find_collision(Bk)
    if collision_index_B is not None:
        return comparator(A, Ak, B, Bk, collision_index_B)

    return False

# Cycle detection function for Ak
def find_collision(Ak):
    seen = set()
    for i, item in enumerate(Ak):
        if item in seen:
            return mpz(i)  # Collision detected, return as mpz
        seen.add(item)
    return None  # No collision detected

# Cycle detection function for Bk
def find_collision(Bk):
    seen = set()
    for i, item in enumerate(Bk):
        if item in seen:
            return mpz(i)  # Collision detected, return as mpz
        seen.add(item)
    return None  # No collision detected

# Function to compare two sets of points and find a common point
def comparator(A, Ak, B, Bk, collision_index):
    global STOP_EVENT
    result = set(A).intersection(set(B))
    if result:
        sol_kt = A.index(next(iter(result)))
        sol_kw = B.index(next(iter(result)))
        difference = Ak[sol_kt] - Bk[sol_kw]
        HEX = "%064x" % difference
        wifc = ice.btc_pvk_to_wif("%064x" % mpz(difference))
        dec = int(ice.btc_wif_to_pvk_hex(wifc), 16)
        wifu = ice.btc_pvk_to_wif(HEX, False)  # Uncompressed
        uaddr = ice.privatekey_to_address(0, False, dec)  # Uncompressed
        caddr = ice.privatekey_to_address(0, True, dec)  # Compressed
        HASH160 = ice.privatekey_to_h160(0, True, dec).hex()
        t = time.ctime()
        total_time = time.time() - starttime
        print(f"\033[32m[+] PUZZLE SOLVED: {t}, total time: {total_time:.2f} sec \033[0m")
        print(f"\033[32m[+] WIF: \033[32m {wifc} \033[0m")
        with open("KEYFOUNDKEYFOUND.txt", "a") as file:
            file.write("\n\nPUZZLE SOLVED " + t)
            file.write(f"\nTotal Time: {total_time:.2f} sec")
            file.write('\nPrivate Key (dec): ' + str(dec))
            file.write('\nPrivate Key (hex): ' + HEX)
            file.write('\nPrivate Key Compressed: ' + wifc)
            file.write('\nPrivate Key Uncompressed: ' + wifu)
            file.write('\nPublic Address Compressed: ' + caddr)
            file.write('\nPublic Address Uncompressed: ' + uaddr)
            file.write('\nPublic Key Hash Compressed (Hash 160): ' + HASH160)
            file.write(
                "\n-------------------------------------------------------------------------------------------------------------------------------------\n"
            )

        STOP_EVENT.set()  # Set the stop event to signal all processes

# Memoization for point multiplication
ECMULTIPLY_MEMO = {}

# Function to multiply a point by a scalar
def ecmultiply(k, P=PG, p=MODULO):
    if k == 0:
        return ZERO_POINT
    elif k == 1:
        return P
    elif k % 2 == 0:
        if k in ECMULTIPLY_MEMO:
            return ECMULTIPLY_MEMO[k]
        else:
            result = ecmultiply(k // 2, multiply_by_2(P, p), p)
            ECMULTIPLY_MEMO[k] = result
            return result
    else:
        return add_points(P, ecmultiply((k - 1) // 2, multiply_by_2(P, p), p))

# Recursive function to multiply a point by a scalar
def mulk(k, P=PG, p=MODULO):
    if k == 0:
        return ZERO_POINT
    elif k == 1:
        return P
    elif k % 2 == 0:
        return mulk(k // 2, multiply_by_2(P, p), p)
    else:
        return add_points(P, mulk((k - 1) // 2, multiply_by_2(P, p), p))

# Generate a list of powers of two for faster access
@lru_cache(maxsize=None)
def generate_powers_of_two(hop_modulo):
    return [mpz(1 << pw) for pw in range(hop_modulo)]

# Worker function for point search
def search_worker(Nt, Nw, puzzle, tortoise_power, starttime, lower_range_limit, upper_range_limit):
    global STOP_EVENT

    random_state_t = gmpy2.random_state(hash(gmpy2.random_state()))
    random_state_w = gmpy2.random_state(hash(gmpy2.random_state()))

    t = [mpz(lower_range_limit + gmpy2.mpz_random(random_state_t, upper_range_limit - lower_range_limit)) for _ in range(Nt)]
    T = [mulk(ti) for ti in t]
    dt = [mpz(0) for _ in range(Nt)]
   
    w = [gmpy2.mpz_random(random_state_w, upper_range_limit - lower_range_limit) for _ in range(Nt)]
    W = [add_points(W0, mulk(wk)) for wk in w]
    dw = [mpz(0) for _ in range(Nw)]

    Hops, Hops_old = 0, 0

    oldtime = time.time()
    starttime = oldtime

    while True:
        for k in range(Nt):
            Hops += 1
            pw = T[k].x % hop_modulo
            dt[k] = powers_of_two[pw]
            solved = check(T[k], t[k], DP_rarity, T, t, W, w)
            if solved:
                STOP_EVENT.set()
                raise SystemExit
            t[k] += dt[k]
            T[k] = add_points(POINTS_TABLE[pw], T[k])

        for k in range(Nw):
            Hops += 1
            pw = W[k].x % hop_modulo
            dw[k] = powers_of_two[pw]
            solved = check(W[k], w[k], DP_rarity, W, w, T, t)
            if solved:
                STOP_EVENT.set()
                raise SystemExit
            w[k] += dw[k]
            W[k] = add_points(POINTS_TABLE[pw], W[k])

        if STOP_EVENT.is_set():
            break


# Main script
if __name__ == "__main__":
    os.system("clear")
    t = time.ctime()
    sys.stdout.write("\033[01;33m")
    sys.stdout.write(f"[+] {t}" + "\n")
    sys.stdout.write(f"[+] Cycle detected, applying Floyd's cycle-finding algorithm..." + "\n")
    sys.stdout.flush()
    # Configuration for the puzzle
    puzzle = 50
    compressed_public_key = "03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6"  # Puzzle 50
    lower_range_limit = 2 ** (puzzle - 1)
    upper_range_limit = (2 ** puzzle) - 1
    tortoise_power = puzzle // 8
    Nt = Nw = (2 ** tortoise_power // puzzle) * puzzle + 8
    DP_rarity = 8 * puzzle
    hop_modulo = (puzzle // 2) + 8

    # Precompute powers of two for faster access
    powers_of_two = generate_powers_of_two(hop_modulo)

    T, t, dt = [], [], []
    W, w, dw = [], [], []

    if len(compressed_public_key) == 66:
        X = mpz(compressed_public_key[2:66], 16)
        Y = x_to_y(X, mpz(compressed_public_key[:2]) - 2)
    else:
        print("[error] pubkey len(66/130) invalid!")

    print(f"[+] [Puzzle]: {puzzle}")
    print(f"[+] [Lower range limit]: {lower_range_limit}")
    print(f"[+] [Upper range limit]: {upper_range_limit}")
    print("[+] [Xcoordinate]: %064x" % X)
    print("[+] [Ycoordinate]: %064x" % Y)

    W0 = Point(X, Y)
    starttime = oldtime = time.time()

    Hops = 0

    process_count = cpu_count()
    print(f"[+] Using {process_count} CPU cores for parallel search")

    # Create a pool of worker processes
    pool = Pool(process_count)
    results = pool.starmap(
        search_worker,
        [
            (
                Nt,
                Nw,
                puzzle,
                tortoise_power,
                starttime,
                lower_range_limit,
                upper_range_limit,
            )
        ]
        * process_count,
    )
    pool.close()
    pool.join()
copper member
Activity: 1330
Merit: 899
🖤😏
if you do your math well
the normal time for scanning the 66 bit range on an average cpu would take nothing less than 500 years

28 months running 1000 GPUs, nonstop.


Imagine that electricity bill at the household rate. Grin
Ok then, lets materialize our imagination, if we want to be fair, we should say the time taken to solve 66 is 14 month and not 28 month. Right?

14 month = 420 days.
420 days = 10080 hours.
RTX 3090 rental price = $0.20/hr
1 RTX 3090 rented for 10080 hours = $2016
1000 RTX 3090 rented for 10080 hours = $2016000
We will use a discounted price of $2,000,000 USD.

Puzzle 66 contains 6.6 bitcoins, at a price of $30,000. 6.6 * 30,000 = $198000.

In the future when bitcoin is at $100,000 * 6.6 = $660,000.
Further in the future, bitcoin is at $300,000. tech has advanced, speed of key per second is now 10B/s. So we just need 100 GPUs.
100 * $2016 = $201,600. With discount is $200,000 cost of finding #66, and bitcoin at 300k, now puzzle reward is worth $1,980,000 - 200,000 = $1,780,000 profit.

Question, when will we see GPU speed jumping from 1B/s to 10B/s, and bitcoin price at $300,000?
Even then what about #67, two times harder than #66 in theory, #68, #69, #70?


It's either profitable to rent GPUs and find them, or will be profitable in the future, in both cases, nothing really changes, nobody is gonna come up with a solution and an algorithm to brute force keys with 10B/s rate in the next 10 years.

member
Activity: 503
Merit: 38
if you do your math well
the normal time for scanning the 66 bit range on an average cpu would take nothing less than 500 years

28 months running 1000 GPUs, nonstop.


Imagine that electricity bill at the household rate. Grin
hero member
Activity: 630
Merit: 731
Bitcoin g33k
Since Puzzle 64 was found 2022-09-10
does that mean it's been a year now 2023-09-20 and still the search for Puzzle 66 on going?!
puzzle #64 was found on 2022-09-09
puzzle #65 was found already on 2019-06-07
puzzle #120 was found on 2023-02-27 but the privkey was not revealed yet

correct, puzzle #66 is still ongoing ...
copper member
Activity: 1330
Merit: 899
🖤😏
if you do your math well
the normal time for scanning the 66 bit range on an average cpu would take nothing less than 500 years

How did you calculate to get 500 years? An average cpu is useless, with a good GPU you could get 1 billion keys per second which would take 2339 years to search the entire 66 bit range, even if we divide that time by half, we get 1169 years with an average GPU. If you run 1000 GPUs each at 1B/s for more than 14 months, you could search half the range of 2^66, if the key is not there, you'd need to keep searching, in total to make sure 66 bit is searched completely, 28 months running 1000 GPUs, nonstop.

Also addresses with revealed pubs are not solvable every 4 to 6 month, if you are using kangaroo, it will get more difficult as each key range is 32 times larger than the previous key range, unless you know some tricks to reduce this range, otherwise you should at least multiply the time by 8, so if 125 took 4 months, 130 should take around 32 months. Then even if we double that time, we can expect 135 to take at least 64 months.


Note, my calculations are estimates.
jr. member
Activity: 75
Merit: 5

I really appreciate your advice, yeah i was just wondering if i could try my luck with all the unsolved addresses at once lmao.

If anyone can correct me with this:
Since Puzzle 64 was found 2022-09-10
does that mean it's been a year now 2023-09-20 and still the search for Puzzle 66 on going?!

if you do your math well
the normal time for scanning the 66 bit range on an average cpu would take nothing less than 500 years
now we have pools that are scanning and they're barely 4% gone on the 66 bit range...
so this is to tell you that you should expect at least another 1 or 2 more years for the 66 bit range to be defeated successfully
but the pubkey ranges might be the appropriate range of choice for you as these gets defeated almost every 4 to 6 months
newbie
Activity: 4
Merit: 0

I really appreciate your advice, yeah i was just wondering if i could try my luck with all the unsolved addresses at once lmao.

If anyone can correct me with this:
Since Puzzle 64 was found 2022-09-10
does that mean it's been a year now 2023-09-20 and still the search for Puzzle 66 on going?!
copper member
Activity: 1330
Merit: 899
🖤😏
Question please: I took the unsolved address from a text file in keyhunt i think there's about 80 addresses in there,
and tried to pass them to cubitcrack using -i to read from the file but it always gives me an error, but if the files has just few address cubitcrack works!
any idea how to fix that or work around it?
I never used any of those tools, but if you are searching for puzzle 66 in 66 bit range, you only need puzzle 66 address, unless you are trying to search a larger range which is pointless and a waste of your time and resources.

I have said this many times, brute forcing for addresses with no good hardware is futile. If you want to test your "luck" try buying a lottery ticket with closed eyes, just pick randomly, you'd have billion times more chance to win than finding a key using a gaming pc.

Last year, I was like you, I spent 4 months trying to find addresses, but then I realized there is a shortcut which is elliptic curve and math, you should skip this stage and mutate into the higher stages, learn how to do EC operations, division, subtraction etc.

If someone had told me this back then, I would be 4, 5 months ahead. Not that changes anything, but my time would not be wasted.
newbie
Activity: 4
Merit: 0

Oh hey Barry (west allen ) lol.  Welcome to the jungle club of pointless puzzle hunters.
in the flash, not the fastest tho probably not the brightest too lol

Now I have questions, what hardware do you have? just an old gaming computer i5 4 cores, 16 gb ram and 1060 6gb which surprisingly still run all the games!
What have you learned about elliptic curve cryptography? i know absolutely nothing about it, i always see people here talking about it and doing some math i couldn't get.

Thank you for the tips, I think it's not just a challenge made by one person but a security test from some big entity whom are already deep in bitcoin or willing to go deeper!
Also this challenge made me worried  Huh about my own wallets if some lucky gpu "boi" scanning the whole addresses trying to get lucky.
But then knowing i've been trying to get lucky myself for the past month with just "puzzle" 66 and it's very hard gave me q relief  Cheesy knowing it's super difficult!

i always get about $200 in electricity bill, the past month using keyhunt and bitcrack every night keeping the computer on the bill came with $400

Question please: I took the unsolved address from a text file in keyhunt i think there's about 80 addresses in there,
and tried to pass them to cubitcrack using -i to read from the file but it always gives me an error, but if the files has just few address cubitcrack works!
any idea how to fix that or work around it?
copper member
Activity: 1330
Merit: 899
🖤😏
I think the puzzle creator should reveal the public keys for all the keys bigger than 120bit except 124, 134, 144, 154, this will make the challenge reflect the true strength of bitcoin security.
I'm not sure if a gold hoarding dragon is interested to risk more than what is already at risk. Lol.
But seriously if he wants to keep the coins there forever and not touch them, it would be better to move them to exposed public keys, because address brute force is practically pointless, unless he intends to wait and see what happens to technology 40 years from now, so practically unexposed keys above 80 bits serving no purpose, while as you said it, moving those coins to public keys would increase the incentive for new bloods joining this challenge.

I'm guessing he has either left the community "again" like in 2010 ( edit: this part was unfair, recently he increased the prize, I regret saying this part ), or he doesn't care about anything other than his fortune, as per usual.



We only live once, if we don't jump we'll never find out, that's why they call it leap of faith. You only find out after jumping.😉
jr. member
Activity: 40
Merit: 8
I think the puzzle creator should reveal the public keys for all the keys bigger than 120bit except 124, 134, 144, 154, this will make the challenge reflect the true strength of bitcoin security.
copper member
Activity: 1330
Merit: 899
🖤😏


Code:
import SECP256k1
import Point
import sha256
import Int
import ripemd160
import boost.multiprecision.cpp_int as cpp_int
import gmpy2 as mpz
import math
import time
import threading
import os

START_VALUE = 576565752303423488
END_VALUE = 900000000000000000
INCREMENT = 1

# Define a range of factors
MIN_FACTOR = 64.0
MAX_FACTOR = 1028.0
FACTOR_INCREMENT = 1.00

currentValue = mpz.mpz(START_VALUE)
totalKeys = 0
printMutex = threading.Lock()
resultMutex = threading.Lock()
ripemd160Hashes = []

startTime = None
matchFound = False
currentHexPrivateKey = ""

def loadRIPEMD160Hashes():
    with open("wallets.txt", "r") as file:
        for line in file:
            hexHash = line.strip()
            if len(hexHash) != 40:
                print(f"Invalid RIPEMD160 hash length: {len(hexHash)}")
                continue

            hash = bytearray.fromhex(hexHash)
            ripemd160Hashes.append(hash)

        print(f"Loaded {len(ripemd160Hashes)} RIPEMD160 hashes from file.")

def hexBytesToHexString(bytes):
    return "".join([format(b, "02x") for b in bytes])

def hasMinimumMatchingCharacters(hash):
    for loadedHash in ripemd160Hashes:
        isMatch = True
        for j in range(19): # Loop through the first 5 bytes (40 bits)
            if hash[j] != loadedHash[j]:
                isMatch = False
                break # If any character doesn't match, stop checking
       
        if isMatch:
            return True
   
    return False

def printProgress():
    global startTime
    startTime = time.time()

    while not matchFound:
        elapsed = time.time() - startTime
        keysPerSecond = totalKeys / elapsed if elapsed != 0 else 0

        with resultMutex:
            print(f"\rTime: {int(elapsed)}s, Keys: {totalKeys}, Keys/s: {round(keysPerSecond, 5)}, Current: {currentValue}, Priv Key: {currentHexPrivateKey}", end="")
        time.sleep(5)


def counterWorker(threadId, secp256k1, numThreads):
    global currentHexPrivateKey, matchFound, startTime, totalKeys, currentValue
    current = mpz.mpz(START_VALUE + threadId * INCREMENT)

    while current <= END_VALUE:
        for factor in range(int(MIN_FACTOR), int(MAX_FACTOR) + 1, int(FACTOR_INCREMENT)):
            result = current * int(factor)
            hexPrivateKey = format(int(result), "x") # Get hex representation directly
            currentHexPrivateKey = hexPrivateKey

            privateKey = Int.Int(0)
            privateKey.SetBase16(hexPrivateKey)

            publicKey = secp256k1.ComputePublicKey(privateKey)

            compressedPublicKey = bytearray(secp256k1.GetPublicKeyRaw(True, publicKey))
            publicKeyHash = sha256.sha256(compressedPublicKey)

            ripemd160Hash = ripemd160.ripemd160(publicKeyHash)

            if hasMinimumMatchingCharacters(ripemd160Hash):
                matchedPrivateKey = hexPrivateKey # Store the private key for printing
                matchedRipemd160 = hexBytesToHexString(ripemd160Hash) # Convert RIPEMD160 to hex string

                with printMutex:
                    print(f"\nMatching RIPEMD160 hash found. Private Key: {matchedPrivateKey}, RIPEMD160: {matchedRipemd160}")

                with open("found.txt", "a") as foundFile:
                    foundFile.write(f"Matched Private Key: {matchedPrivateKey}, RIPEMD160: {matchedRipemd160}\n")

                matchFound = True
                break
       
        totalKeys += 1

        # Update the currentValue atomically
        with resultMutex:
            currentValue = current

        current = current + (INCREMENT * numThreads)

    # Signal that this thread has completed its work
    with resultMutex:
        matchFound = True

def main():
    global matchFound, totalKeys, currentValue

    loadRIPEMD160Hashes()

    secp256k1 = SECP256k1.SECP256k1()
    secp256k1.Init()

    threads = []
    numThreads = os.cpu_count()

    # Start the progress printing thread
    progressThread = threading.Thread(target=printProgress)
    progressThread.start()

    for i in range(numThreads):
        threads.append(threading.Thread(target=counterWorker, args=(i, secp256k1, numThreads)))
   
    for thread in threads:
        thread.start()

    for thread in threads:
        thread.join()

    # Wait for the progress thread to complete
    progressThread.join()

    print()

if __name__ == "__main__":
    main()

Script above is the python version of your code written in alien language, I'm on phone so I couldn't test to see if it works, later I will test it on my laptop and fix any issues. Insha'Allah. ( God willing )

Also;
Thanks for the update on the code, appreciate it. My scripts ( small part of them ) don't need much speed because they are not supposed to auto solve a key, they are intended as learning tools, I talked about improving performance to make king of information stop whining so much. 🤣
member
Activity: 110
Merit: 61
Hi, nice code Smiley The prime numbers approach is a good idea, I also try it some time ago, but it only works as you said if you have the right factors if no then the brute force approach of the prime numbers is also some exhaustive, we can try some small and common factors but those don't provide much speed, but if the prime factors aren't common then the probabilities of some prime number is factor of our key are very low probabilities.

IMHO this prime number approach is a nice as proof of concept but with some low success


I tried and tested similar approach a year ago. The best result I could achieve is O(N/4) complexity for a random number, but only if this number is composite. This is 4x better than bruteforce, but it's requires a lot of scalar multiplications, therefore optimized sequential key generation is still faster despite the higher complexity of O(N).
member
Activity: 503
Merit: 38
copper member
Activity: 1330
Merit: 899
🖤😏
Hi everyone, I've been reading your forum for long about this challenge and trying my luck for a long time without finding anything!
So I've joined you hopefully to work as a group and split the prize.

I've got two questions please:
1) Where can i find the already scanned ranges for puzzle 66? so i won't scan it again!
2) Where can i share the ranges i've already scanned?


Oh hey Barry (west allen ) lol.  Welcome to the jungle club of pointless puzzle hunters.
There are some pools searching in ranges which you can join, but I won't link to them because I'm not sure whether they are safe to use or not, but if you search for the keyword pool on this thread, you will find their posts, look at top right corner search box, search while you are inside this thread.

Now I have questions, what hardware do you have?
What have you learned about elliptic curve cryptography?

I hope you realize this is not a game only for entertainment and finding free bitcoins, the man has spared around $30 million in bitcoin for reasons other than fun and playing riddles with community members.

You should know, this is a world class programming/ mathematical challenge, worth more than 30 nobel prizes combined.🤑😉
newbie
Activity: 4
Merit: 0
Hi everyone, I've been reading your forum for long about this challenge and trying my luck for a long time without finding anything!
So I've joined you hopefully to work as a group and split the prize.

I've got two questions please:
1) Where can i find the already scanned ranges for puzzle 66? so i won't scan it again!
2) Where can i share the ranges i've already scanned?

copper member
Activity: 1330
Merit: 899
🖤😏

Can you please explain in details for us noobs how this works and what we need to change when searching for our desired keys?
Also what do you mean by solving 65 without needing a public key, do you search for address or something?

Do you happen to have a python code for it, even if it's slow. Or isn't it better to ask ai to convert the code into python? Is that even possible?


Edit:

Here is a working script which divides 2 points by start/end range and then subtracts the results of division from each other.
No external module/ library needed, just run the script.

Code:
# Define the EllipticCurve class
class EllipticCurve:
    def __init__(self, a, b, p):
        self.a = a
        self.b = b
        self.p = p

    def contains(self, point):
        x, y = point.x, point.y
        return (y * y) % self.p == (x * x * x + self.a * x + self.b) % self.p

    def __str__(self):
        return f"y^2 = x^3 + {self.a}x + {self.b} mod {self.p}"

# Define the Point class
class Point:
    def __init__(self, x, y, curve):
        self.x = x
        self.y = y
        self.curve = curve

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y and self.curve == other.curve

    def __ne__(self, other):
        return not self == other

    def __add__(self, other):
        if self.curve != other.curve:
            raise ValueError("Cannot add points on different curves")

        # Case when one point is zero
        if self == Point.infinity(self.curve):
            return other
        if other == Point.infinity(self.curve):
            return self

        if self.x == other.x and self.y != other.y:
            return Point.infinity(self.curve)

        p = self.curve.p
        s = 0
        if self == other:
            s = ((3 * self.x * self.x + self.curve.a) * pow(2 * self.y, -1, p)) % p
        else:
            s = ((other.y - self.y) * pow(other.x - self.x, -1, p)) % p

        x = (s * s - self.x - other.x) % p
        y = (s * (self.x - x) - self.y) % p

        return Point(x, y, self.curve)

    def __sub__(self, other):
        if self.curve != other.curve:
            raise ValueError("Cannot subtract points on different curves")

        # Case when one point is zero
        if self == Point.infinity(self.curve):
            return other
        if other == Point.infinity(self.curve):
            return self

        return self + Point(other.x, (-other.y) % self.curve.p, self.curve)

    def __mul__(self, n):
        if not isinstance(n, int):
            raise ValueError("Multiplication is defined for integers only")

        n = n % (self.curve.p - 1)
        res = Point.infinity(self.curve)
        addend = self

        while n:
            if n & 1:
                res += addend

            addend += addend
            n >>= 1

        return res

    def __str__(self):
        return f"({self.x}, {self.y}) on {self.curve}"

    @staticmethod
    def from_hex(s, curve):
        if len(s) == 66 and s.startswith("02") or s.startswith("03"):
            compressed = True
        elif len(s) == 130 and s.startswith("04"):
            compressed = False
        else:
            raise ValueError("Hex string is not a valid compressed or uncompressed point")

        if compressed:
            is_odd = s.startswith("03")
            x = int(s[2:], 16)

            # Calculate y-coordinate from x and parity bit
            y_square = (x * x * x + curve.a * x + curve.b) % curve.p
            y = pow(y_square, (curve.p + 1) // 4, curve.p)
            if is_odd != (y & 1):
                y = -y % curve.p

            return Point(x, y, curve)
        else:
            s_bytes = bytes.fromhex(s)
            uncompressed = s_bytes[0] == 4
            if not uncompressed:
                raise ValueError("Only uncompressed or compressed points are supported")

            num_bytes = len(s_bytes) // 2
            x_bytes = s_bytes[1 : num_bytes + 1]
            y_bytes = s_bytes[num_bytes + 1 :]

            x = int.from_bytes(x_bytes, byteorder="big")
            y = int.from_bytes(y_bytes, byteorder="big")

            return Point(x, y, curve)

    def to_hex(self, compressed=True):
        if self.x is None and self.y is None:
            return "00"
        elif compressed:
            prefix = "03" if self.y & 1 else "02"
            return prefix + hex(self.x)[2:].zfill(64)
        else:
            x_hex = hex(self.x)[2:].zfill(64)
            y_hex = hex(self.y)[2:].zfill(64)
            return "04" + x_hex + y_hex

    @staticmethod
    def infinity(curve):
        return Point(None, None, curve)

# Define the ec_mul function
def ec_mul(point, scalar, base_point):
    result = Point.infinity(point.curve)
    addend = point

    while scalar:
        if scalar & 1:
            result += addend

        addend += addend
        scalar >>= 1

    return result

# Define the ec_operations function
def ec_operations(start_range, end_range, target_1, target_2, curve):
    # Define parameters for secp256k1 curve
    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    G = Point(
        0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
        0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
        curve
    )

    # Open the files for writing
    with open("target1_division_results.txt", "a") as file1, \
            open("target2_division_results.txt", "a") as file2, \
            open("subtract_results.txt", "a") as file3:

        for i in range(start_range, end_range + 1):
            try:
                # Compute the inverse of i modulo n
                i_inv = pow(i, n-2, n)

                # Divide the targets by i modulo n
                result_1 = ec_mul(target_1, i_inv, G)
                result_2 = ec_mul(target_2, i_inv, G)

                # Subtract the results
                sub_result = result_2 - result_1

                # Write the results to separate files
                file1.write(f"{result_1.to_hex()}\n")
                file2.write(f"{result_2.to_hex()}\n")
                file3.write(f"Subtracting results for {i}:\n")
                file3.write(f"Target 1 / {i}: {result_1.to_hex()}\n")
                file3.write(f"Target 2 / {i}: {result_2.to_hex()}\n")
                file3.write(f"Subtraction: {sub_result.to_hex()}\n")
                file3.write("="*50 + "\n")

                print(f"Completed calculation for divisor {i}")
            except ZeroDivisionError:
                print(f"Error: division by zero for {i}")

    print("Calculation completed. Results saved in separate files.")

if __name__ == "__main__":
    # Set the targets and range for the operations
    curve = EllipticCurve(0, 7, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
    target_1 = Point.from_hex("0230210C23B1A047BC9BDBB13448E67DEDDC108946DE6DE639BCC75D47C0216B1B", curve)
    target_2 = Point.from_hex("02F6B787195159544330085C6014DBA627FF5B14F3203FF05D12482F76261F4FC3", curve)
    start_range = 2
    end_range = 200

    ec_operations(start_range, end_range, target_1, target_2, curve)

Note that on previous page there is one similar script but operating over scalar not points.
Also the credit for the division by a range part goes to @mcdouglasx, the rest goes to anyone here helping and of course the biggest idiot AI made by mankind ( all of them are for now ) aka deep.ai chatbot.

As a test sample I have used puzzle 65 in target1 and target 2 is an offset after subtracting the following scalar from puzzle 65.
0x0000000000000000000000000000000000000000000000020000000000000000

The script is really slow because I had problem using multiprocessing  in the code, so I decided to remove it, it also first calculates everything and then writes them to the files, since I'm not a coder, I don't know whether doing that requires more RAM or not.

Feel free to optimize, improve and add other functions/operations as you see fit and please do share.
Thanks.
hero member
Activity: 862
Merit: 662
Now I can solve puzzle 65 without the public key in just a few seconds if you have the right factors in place

Hi, nice code Smiley The prime numbers approach is a good idea, I also try it some time ago, but it only works as you said if you have the right factors if no then the brute force approach of the prime numbers is also some exhaustive, we can try some small and common factors but those don't provide much speed, but if the prime factors aren't common then the probabilities of some prime number is factor of our key are very low probabilities.

IMHO this prime number approach is a nice as proof of concept but with some low success
jr. member
Activity: 75
Merit: 5
but it is excellent, even better than C for testing. of new ideas,

I agree with this, personally i think that testing and proof of concept are the only thing that python is good for. But once that the proof of concept is proven to be useful or good then is time to move to another language. If speed is determining for the algorithm


the fact that you think that there are no new things is simply the result of your own mental fatigue

What F.. mental Fatigue? I already shared here a lot of programs, ideas, users share with me their ideas on telegram and there is nothing new...

What i am tryting to said is if you have a vague idea without math background or without enough testing, then OPEN a new thread discuss their idea there and if the idea was proven to be good, then link that new topic here sharing a brief sumary of it and how to use.

I have this code already which is not an idea for now but a work in progress but we might be needing to know if it's a good idea

Code:
#include "SECP256k1.h"
#include "Point.h"
#include "sha256.h"
#include "Int.h"
#include "ripemd160.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

constexpr uint64_t START_VALUE = 576565752303423488ULL;
constexpr uint64_t END_VALUE = 900'000'000'000'000'000ULL;
constexpr uint64_t INCREMENT = 1ULL;

// Define a range of factors
constexpr double MIN_FACTOR = 64.0;
constexpr double MAX_FACTOR = 1028.0;
constexpr double FACTOR_INCREMENT = 1.00;

std::atomic currentValue(START_VALUE);
std::atomic totalKeys(0);
std::mutex printMutex;
std::mutex resultMutex;
std::vector> ripemd160Hashes;

std::chrono::time_point startTime;
std::atomic matchFound(false);
std::string currentHexPrivateKey;

void loadRIPEMD160Hashes() {
    std::ifstream file("wallets.txt");
    if (file.is_open()) {
        std::string hexHash;
        while (file >> hexHash) {
            if (hexHash.length() != 40) {
                std::cerr << "Invalid RIPEMD160 hash length: " << hexHash.length() << std::endl;
                continue; // Skip invalid hashes
            }

            std::array hash;
            for (int i = 0; i < 20; ++i) {
                std::string byteHex = hexHash.substr(2 * i, 2);
                hash[i] = static_cast(std::stoi(byteHex, nullptr, 16));
            }

            ripemd160Hashes.push_back(hash);
        }
        file.close();
        std::cout << "Loaded " << ripemd160Hashes.size() << " RIPEMD160 hashes from file." << std::endl;
    }
}

std::string hexBytesToHexString(const uint8_t* bytes, size_t length) {
    std::stringstream ss;
    ss << std::hex << std::setfill('0');
    for (size_t i = 0; i < length; ++i) {
        ss << std::setw(2) << static_cast(bytes[i]);
    }
    return ss.str();
}

bool hasMinimumMatchingCharacters(const uint8_t (&hash)[20]) {
    for (const std::array& loadedHash : ripemd160Hashes) {
        bool isMatch = true;
        for (int j = 0; j < 19; ++j) {  // Loop through the first 5 bytes (40 bits)
            if (hash[j] != loadedHash[j]) {
                isMatch = false;
                break; // If any character doesn't match, stop checking
            }
        }
        if (isMatch) {
            return true;
        }
    }
    return false;
}

void printProgress() {
    startTime = std::chrono::system_clock::now();

    while (!matchFound.load(std::memory_order_relaxed)) {
        {
            std::lock_guard lock(resultMutex);

            auto now = std::chrono::system_clock::now();
            auto elapsed = std::chrono::duration_cast(now - startTime);
            double keysPerSecond = static_cast(totalKeys.load()) / elapsed.count();

            std::cout << "\rTime: " << elapsed.count() << "s, Keys: " << totalKeys.load()
                      << ", Keys/s: " << std::fixed << std::setprecision(5) << keysPerSecond
                      << ", Current: " << currentValue.load()
                      << ", Priv Key: " << currentHexPrivateKey << std::flush;
        }
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

void counterWorker(int threadId, Secp256K1& secp256k1, int numThreads) {
    mpz_class current(START_VALUE + threadId * INCREMENT);

    while (current <= END_VALUE) {
        for (double factor = MIN_FACTOR; factor <= MAX_FACTOR; factor += FACTOR_INCREMENT) {
            mpz_class result = current * static_cast(factor);

            std::string hexPrivateKey = result.get_str(16); // Get hex representation directly
            currentHexPrivateKey = hexPrivateKey;

            Int privateKey;
            privateKey.SetBase16(hexPrivateKey.c_str());

            Point publicKey = secp256k1.ComputePublicKey(&privateKey);

            uint8_t compressedPublicKey[33];
            secp256k1.GetPublicKeyRaw(true, publicKey, reinterpret_cast(compressedPublicKey));

            uint8_t publicKeyHash[32];
            sha256_33(compressedPublicKey, publicKeyHash);

            uint8_t ripemd160Hash[20];
            ripemd160(publicKeyHash, 32, ripemd160Hash);

            if (hasMinimumMatchingCharacters(ripemd160Hash)) {
                std::string matchedPrivateKey = hexPrivateKey; // Store the private key for printing
                std::string matchedRipemd160 = hexBytesToHexString(ripemd160Hash, 20); // Convert RIPEMD160 to hex string

                {
                    std::lock_guard lock(printMutex);
                    std::cout << "\nMatching RIPEMD160 hash found. Private Key: " << matchedPrivateKey << ", RIPEMD160: " << matchedRipemd160 << std::endl;
                }

                {
                    std::ofstream foundFile("found.txt", std::ios::app); // Append mode
                    if (foundFile.is_open()) {
                        foundFile << "Matched Private Key: " << matchedPrivateKey << ", RIPEMD160: " << matchedRipemd160 << std::endl;
                        foundFile.close();
                    }
                }
            }
        }

        totalKeys.fetch_add(1);

        // Convert the mpz_class to uint64_t and update the currentValue atomically
        currentValue.store(static_cast(current.get_ui()), std::memory_order_relaxed);

        current += INCREMENT * numThreads;
    }

    // Signal that this thread has completed its work
    matchFound.store(true, std::memory_order_relaxed);
}

int main() {
    loadRIPEMD160Hashes();

    Secp256K1 secp256k1;
    secp256k1.Init();

    std::vector threads;

    // Start the progress printing thread
    std::thread progressThread(printProgress);

    const int numThreads = std::thread::hardware_concurrency();
    for (int i = 0; i < numThreads; ++i) {
        threads.emplace_back(counterWorker, i, std::ref(secp256k1), numThreads);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    // Wait for the progress thread to complete
    progressThread.join();

    std::cout << std::endl;

    return 0;
}

now I get 1.5million/s if I have 1 factor on my 16 threaded pc which I normally get nothing less than 55 million/s on keyhunt compress and exhomorphism
the more the factor to be multiplied the less the keys/s
and come to think of it, so many private keys are just numbers and these numbers have factors except it's a prime number
Now I can solve puzzle 65 without the public key in just a few seconds if you have the right factors in place
we need to see if we can get this code working on GPU which will also make so much momentum and also if we can get it faster on cpu
it's just an idea in progress and I don't know if it's a good idea that's why I have decided to put it on here
copper member
Activity: 1330
Merit: 899
🖤😏
Oh, look another useless and extremely slow python script which can only be used to test and then solve puzzle keys.😉,
Idea was mine, I had to make a few changes, but the code is generated by the use of deep.ai chat bot. And I have no other way than copy paste it here, so don't take offence for it.

Code:
from sympy import mod_inverse

N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

def ters(scalar, target):
    k = mod_inverse(2, N)
    scalar_bin = bin(scalar)[2:]
    for i in range(len(scalar_bin)):
        if scalar_bin[i] == '0':
            result = (target * k) % N
        else:
            result = ((target * k) % N + N - 1) % N
        target = result
    return result

target1 = 508467739815150526153708316710194877073
target2 = 73909945130129581315154379935980065

print("Target results:")
for x in range(1, 10):
    result1 = ters(x, target1)
    print(f"{x}-T1: {result1:x}")
for x in range(1, 10):
    result2 = ters(x, target2)
    print(f"{x}-T2: {result2:x}")
for x in range(1, 10):
    result1 = ters(x, target1)
    result2 = ters(x, target2)
    subtraction = (result1 - result2) % N
    print(f"{x}-Sub: {subtraction:x}")

Think of target 1 as a puzzle key, and target 2 is known, if we subtract target2 from target1 ( puzzle key) before using the script, we would have this third ( also unknown ) key :
508393829870020396572393162330258897008

And yeah, that's it, I'm not gonna keep talking to actually reach the real puzzle key.😅



I hope nobody takes any offence because I talk too much, if they do, IDRC. 😘

Ps, I'm wearing it as avatar @MisterZz
Jump to: