Post
Topic
Board Bitcoin Discussion
Re: Bitcoin puzzle transaction ~32 BTC prize to who solves it
by
nomachine
on 30/03/2025, 17:33:17 UTC
Maybe someone will find my idea attractive and implement it, thereby speeding up finding solutions to puzzles!

Ok...Here is simplest starting code for this.


Code:
sudo apt update && sudo apt upgrade -y && sudo apt install -y build-essential libssl-dev libboost-all-dev openssl libsecp256k1-dev git cmake

mutagen.cpp
Code:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <queue>
#include <mutex>
#include <unordered_map>
#include <cmath>
#include <fstream>
#include <openssl/ec.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <openssl/bn.h>
#include <openssl/obj_mac.h>
#include <openssl/evp.h>

using namespace std;

// Configuration
const string TARGET_HASH160 = "b907c3a2a3b27789dfb509b730dd47703c272868";
const string BASE_KEY = "000000000000000000000000000000000000000000000000000000000005749f";
const int PUZZLE_NUM = 20;
const int WORKERS = thread::hardware_concurrency();

// Historical flip counts
const unordered_map<int, int> FLIP_TABLE = {
    {20, 8},         {21, 9},     {22, 11},     {23, 12},     {24, 9},       {25, 12},     {26, 14},     {27, 13},
    {28, 16},     {29, 18},     {30, 16},     {31, 13},     {32, 14},     {33, 15},     {34, 16},     {35, 19},
    {36, 14},     {37, 23},     {38, 21},     {39, 23},     {40, 20},     {41, 25},     {42, 24},     {43, 19},
    {44, 24},     {45, 21},     {46, 24},     {47, 27},     {48, 21},     {49, 30},     {50, 29},     {51, 25},
    {52, 27},     {53, 26},     {54, 30},     {55, 31},     {56, 31},     {57, 33},     {58, 28},     {59, 30},
    {60, 31},     {61, 25},     {62, 35},     {63, 34},     {64, 34},     {65, 37},     {66, 35},     {67, 31},
    {68, 34}
};

// Global variables
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, size_t, int>> results;

// Predict flip count
int predict_flips(int puzzle_num) {
    if (FLIP_TABLE.count(puzzle_num)) {
        return FLIP_TABLE.at(puzzle_num);
    }

    vector<int> x, y;
    for (const auto& [key, value] : FLIP_TABLE) {
        x.push_back(key);
        y.push_back(value);
    }

    double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
    for (size_t i = 0; i < x.size(); ++i) {
        sum_x += x[i];
        sum_y += y[i];
        sum_xy += x[i] * y[i];
        sum_xx += x[i] * x[i];
    }

    double slope = (x.size() * sum_xy - sum_x * sum_y) / (x.size() * sum_xx - sum_x * sum_x);
    double intercept = (sum_y - slope * sum_x) / x.size();
    int predicted = round(slope * puzzle_num + intercept);

    int bit_length = puzzle_num;
    int lower = max(8, static_cast<int>(bit_length * 0.5));
    int upper = min(static_cast<int>(bit_length * 0.6), bit_length - 5);
    return min(max(predicted, lower), upper);
}

// Binomial coefficient
size_t combinations_count(int n, int k) {
    if (k > n) return 0;
    size_t result = 1;
    for (int i = 1; i <= k; ++i) {
        result *= (n - i + 1);
        result /= i;
    }
    return result;
}

// Generate combinations
vector<vector<int>> generate_combinations(int n, int k) {
    vector<vector<int>> combinations;
    vector<int> current(k, 0);
    for (int i = 0; i < k; ++i) current[i] = i;

    while (true) {
        combinations.push_back(current);

        int i = k - 1;
        while (i >= 0 && current[i] == n - k + i) --i;
        if (i < 0) break;

        ++current[i];
        for (int j = i + 1; j < k; ++j) current[j] = current[j - 1] + 1;
    }
    return combinations;
}

// Mutate key
uint64_t mutate_key(uint64_t base_int, const vector<int>& flip_positions) {
    uint64_t flip_mask = 0;
    for (int pos : flip_positions) flip_mask |= (1ULL << pos);
    return base_int ^ flip_mask;
}

// Compute HASH160
string compute_hash160(EC_KEY* ec_key) {
    const EC_GROUP* group = EC_KEY_get0_group(ec_key);
    const EC_POINT* pub_key = EC_KEY_get0_public_key(ec_key);

    if (!group || !pub_key) {
        cerr << "Invalid EC key components" << endl;
        return "";
    }

    size_t pub_key_len = EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_COMPRESSED, nullptr, 0, nullptr);
    if (pub_key_len == 0) {
        cerr << "Failed to get public key length" << endl;
        return "";
    }

    vector<unsigned char> pub_key_bytes(pub_key_len);
    if (!EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_COMPRESSED, pub_key_bytes.data(), pub_key_len, nullptr)) {
        cerr << "Failed to serialize public key" << endl;
        return "";
    }

    unsigned char sha256_result[SHA256_DIGEST_LENGTH];
    SHA256(pub_key_bytes.data(), pub_key_bytes.size(), sha256_result);

    unsigned char ripemd160_result[RIPEMD160_DIGEST_LENGTH];
    RIPEMD160(sha256_result, SHA256_DIGEST_LENGTH, ripemd160_result);

    stringstream ss;
    for (int i = 0; i < RIPEMD160_DIGEST_LENGTH; ++i) {
        ss << hex << setw(2) << setfill('0') << static_cast<int>(ripemd160_result[i]);
    }
    return ss.str();
}

// Worker function
void worker(uint64_t base_int, int bit_length, int flip_count, size_t start_index, size_t end_index) {
    size_t checked = 0;
    auto combinations = generate_combinations(bit_length, flip_count);

    EC_KEY* ec_key = EC_KEY_new();
    if (!ec_key) {
        cerr << "Failed to create EC_KEY" << endl;
        return;
    }

    EC_GROUP* ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
    if (!ec_group) {
        cerr << "Failed to create EC_GROUP (secp256k1 not available?)" << endl;
        EC_KEY_free(ec_key);
        return;
    }

    if (!EC_KEY_set_group(ec_key, ec_group)) {
        cerr << "Failed to set EC group" << endl;
        EC_GROUP_free(ec_group);
        EC_KEY_free(ec_key);
        return;
    }

    for (size_t i = start_index; i < end_index && i < combinations.size(); ++i) {
        if (stop_event.load()) break;

        const auto& flip_pos = combinations[i];
        uint64_t priv_int = mutate_key(base_int, flip_pos);
        ++checked;

        BIGNUM* bn_priv = BN_new();
        if (!bn_priv) {
            cerr << "Failed to create BIGNUM" << endl;
            continue;
        }

        if (!BN_set_word(bn_priv, priv_int)) {
            cerr << "Failed to set private key value" << endl;
            BN_free(bn_priv);
            continue;
        }

        if (!EC_KEY_set_private_key(ec_key, bn_priv)) {
            cerr << "Failed to set private key" << endl;
            BN_free(bn_priv);
            continue;
        }

        EC_POINT* pub_key = EC_POINT_new(ec_group);
        if (!pub_key) {
            BN_free(bn_priv);
            continue;
        }

        if (!EC_POINT_mul(ec_group, pub_key, bn_priv, nullptr, nullptr, nullptr)) {
            EC_POINT_free(pub_key);
            BN_free(bn_priv);
            continue;
        }

        if (!EC_KEY_set_public_key(ec_key, pub_key)) {
            EC_POINT_free(pub_key);
            BN_free(bn_priv);
            continue;
        }

        string hash160 = compute_hash160(ec_key);
        EC_POINT_free(pub_key);
        BN_free(bn_priv);

        if (hash160.empty()) {
            continue;
        }

        if (hash160 == TARGET_HASH160) {
            lock_guard<mutex> lock(result_mutex);
            results.push(make_tuple(to_string(priv_int), checked, flip_count));
            stop_event.store(true);
            break;
        }

        if (checked % 10000 == 0) {
            cout << "Checked " << checked << "\r";
            cout.flush();
        }
    }

    EC_GROUP_free(ec_group);
    EC_KEY_free(ec_key);
}

// Parallel search
void parallel_search() {
    int bit_length = PUZZLE_NUM;
    uint64_t base_int = stoull(BASE_KEY, nullptr, 16);
    int flip_count = predict_flips(PUZZLE_NUM);
    size_t total_combs = combinations_count(bit_length, flip_count);

    cout << "Searching Puzzle " << PUZZLE_NUM << " (256-bit)" << endl;
    cout << "Base Key: " << BASE_KEY << endl;
    cout << "Target HASH160: " << TARGET_HASH160 << endl;
    cout << "Predicted Flip Count: " << flip_count << " bits" << endl;
    cout << "Total Possible Combinations: " << total_combs << endl;
    cout << "Using " << WORKERS << " workers..." << endl;

    vector<thread> threads;
    size_t chunk_size = total_combs / WORKERS;

    auto start_time = chrono::high_resolution_clock::now();

    for (int i = 0; i < WORKERS; ++i) {
        size_t start_index = i * chunk_size;
        size_t end_index = (i == WORKERS - 1) ? total_combs : (i + 1) * chunk_size;
        threads.emplace_back(worker, base_int, bit_length, flip_count, start_index, end_index);
    }

    for (auto& t : threads) t.join();

    if (!results.empty()) {
        auto [hex_key, checked, actual_flips] = results.front();
        auto elapsed = chrono::duration_cast<chrono::seconds>(chrono::high_resolution_clock::now() - start_time).count();

        cout << "\nFound in " << elapsed / 3600.0 << " hours" << endl;
        cout << "Private Key: " << hex_key << endl;
        cout << "Actual Bit Flips: " << actual_flips << endl;
        cout << "Keys Checked: " << checked << endl;
        cout << "Speed: " << checked / static_cast<double>(elapsed) << " keys/sec" << endl;

        ofstream solution_file("puzzle_" + to_string(PUZZLE_NUM) + "_solution.txt");
        solution_file << hex_key;
        cout << "\nSolution saved to puzzle_" << PUZZLE_NUM << "_solution.txt" << endl;
    } else {
        cout << "\nKey not found. Try adjusting flip count ±2" << endl;
    }
}

int main() {
    // Initialize OpenSSL (modern versions don't need this, but we'll include it for compatibility)
    #if OPENSSL_VERSION_NUMBER < 0x10100000L
    OpenSSL_add_all_algorithms();
    #endif

    int flip_count = predict_flips(PUZZLE_NUM);
    cout << "Predicted flip count for " << PUZZLE_NUM << ": " << flip_count << " bits" << endl;
    parallel_search();

    // Clean up OpenSSL (only needed for older versions)
    #if OPENSSL_VERSION_NUMBER < 0x10100000L
    EVP_cleanup();
    #endif
   
    return 0;
}


Code:
g++ -O3 -march=native -std=c++17 mutagen.cpp -lssl -lcrypto -lpthread -o mutagen


Quote
# ./mutagen
Predicted flip count for 20: 8 bits
Searching Puzzle 20 (256-bit)
Base Key: 000000000000000000000000000000000000000000000000000000000005749f
Target HASH160: b907c3a2a3b27789dfb509b730dd47703c272868
Predicted Flip Count: 8 bits
Total Possible Combinations: 125970
Using 12 workers...

Found in 0.000833333 hours
Private Key: 863317
Actual Bit Flips: 8
Keys Checked: 5167
Speed: 1722.33 keys/sec

Solution saved to puzzle_20_solution.txt



If you want better, you have to do everything yourself from here.  Grin