Bitcoin Forum
April 23, 2026, 10:17:53 AM *
News: Latest Bitcoin Core release: 30.2 [Torrent]
 
   Home   Help Search Login Register More  
Pages: « 1 ... 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 [424] 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 ... 652 »
  Print  
Author Topic: Bitcoin puzzle transaction ~32 BTC prize to who solves it  (Read 380726 times)
Akito S. M. Hosana
Jr. Member
*
Offline Offline

Activity: 420
Merit: 8


View Profile
April 01, 2025, 01:53:29 PM
 #8461

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

I noticed something, let's say if the speed is 75 million per second, then in theory we should go through 50 billion in 15 minutes, but now it takes more time)

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

First, let’s agree on whether it takes 34, 35, 36, or 37 flips.  Tongue

Number of mutation options for 68 bit of 34: 28453041475240576740
Number of mutation options for 68 bit of 35: 27640097433090845976
Number of mutation options for 68 bit of 36: 25336755980333275478
Number of mutation options for 68 bit of 37: 21912870037044995008


Then you need to correct the script so that it doesn't calculate for Flip count: 34
Total combinations: 47478523248093572  Tongue
Denevron
Newbie
*
Offline Offline

Activity: 121
Merit: 0


View Profile
April 01, 2025, 02:19:21 PM
 #8462

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

I noticed something, let's say if the speed is 75 million per second, then in theory we should go through 50 billion in 15 minutes, but now it takes more time)

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

First, let’s agree on whether it takes 34, 35, 36, or 37 flips.  Tongue

Number of mutation options for 68 bit of 34: 28453041475240576740
Number of mutation options for 68 bit of 35: 27640097433090845976
Number of mutation options for 68 bit of 36: 25336755980333275478
Number of mutation options for 68 bit of 37: 21912870037044995008


Then you need to correct the script so that it doesn't calculate for Flip count: 34
Total combinations: 47478523248093572  Tongue

For some reason I get different numbers  Grin
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 04:32:03 PM
 #8463

1. Thread-Based Work Distribution
    Added: Thread management with std::thread
    Changed: Split work into N equal ranges (where N = worker count)
    Benefit: Evenly distributes workload across all CPU cores

2. Thread-Local Randomization
    Added: Per-thread Mersenne Twister RNG (mt19937_64)
    Changed: Fisher-Yates shuffle within each thread's range only
    Benefit: Eliminates contention while maintaining randomness

3. Range-Based Processing
    Added: Explicit start/end indices for each thread
    Removed: Global sequential counter
    Benefit: No shared state between threads

4. Progress Reporting Optimization
    Changed: Time-based (1 second) instead of count-based updates
    Added: Atomic timestamp for coordination
    Benefit: Reduces console I/O overhead

5. Result Handling
    Added: Thread-safe queue for results
    Changed: Early termination when solution found
    Benefit: Fast termination without losing results


Code:
#include <iostream>
#include <array>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <queue>
#include <mutex>
#include <cstring>
#include <unordered_map>
#include <cmath>
#include <immintrin.h>
#include <omp.h>
#include <csignal>
#include <random>
#include <algorithm>
#include <getopt.h>

#ifdef _WIN32
    #include <windows.h>
#endif

// Include the required headers
#include "sha256_avx2.h"
#include "ripemd160_avx2.h"
#include "SECP256K1.h"
#include "Point.h"
#include "Int.h"
#include "IntGroup.h"

using namespace std;

// Cross-platform terminal functions
void initConsole() {
#ifdef _WIN32
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD mode = 0;
    GetConsoleMode(hConsole, &mode);
    SetConsoleMode(hConsole, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#endif
}

void clearTerminal() {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[2J\033[H";
#endif
    std::cout.flush();
}

void moveCursorTo(int x, int y) {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {(SHORT)x, (SHORT)y};
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[" << y << ";" << x << "H";
#endif
    std::cout.flush();
}

// Configuration
int PUZZLE_NUM = 20;
int WORKERS = omp_get_num_procs();
int FLIP_COUNT = -1;
static constexpr int POINTS_BATCH_SIZE = 256;
static constexpr int HASH_BATCH_SIZE = 8;

// Puzzle data
const unordered_map<int, tuple<int, string, string>> PUZZLE_DATA = {
    {20, {8, "b907c3a2a3b27789dfb509b730dd47703c272868", "357535"}},
    {21, {9, "29a78213caa9eea824acf08022ab9dfc83414f56", "863317"}},
    {22, {11, "7ff45303774ef7a52fffd8011981034b258cb86b", "1811764"}},
    {23, {12, "d0a79df189fe1ad5c306cc70497b358415da579e", "3007503"}},
    {24, {9, "0959e80121f36aea13b3bad361c15dac26189e2f", "5598802"}},
    {25, {12, "2f396b29b27324300d0c59b17c3abc1835bd3dbb", "14428676"}},
    {26, {14, "bfebb73562d4541b32a02ba664d140b5a574792f", "33185509"}},
    {27, {13, "0c7aaf6caa7e5424b63d317f0f8f1f9fa40d5560", "54538862"}},
    {28, {16, "1306b9e4ff56513a476841bac7ba48d69516b1da", "111949941"}},
    {29, {18, "5a416cc9148f4a377b672c8ae5d3287adaafadec", "227634408"}},
    {30, {16, "d39c4704664e1deb76c9331e637564c257d68a08", "400708894"}},
    {31, {13, "d805f6f251f7479ebd853b3d0f4b9b2656d92f1d", "1033162084"}},
    {32, {14, "9e42601eeaedc244e15f17375adb0e2cd08efdc9", "2102388551"}},
    {33, {15, "4e15e5189752d1eaf444dfd6bff399feb0443977", "3093472814"}},
    {34, {16, "f6d67d7983bf70450f295c9cb828daab265f1bfa", "7137437912"}},
    {35, {19, "f6d8ce225ffbdecec170f8298c3fc28ae686df25", "14133072157"}},
    {36, {14, "74b1e012be1521e5d8d75e745a26ced845ea3d37", "20112871792"}},
    {37, {23, "28c30fb11ed1da72e7c4f89c0164756e8a021d", "42387769980"}},
    {38, {21, "b190e2d40cfdeee2cee072954a2be89e7ba39364", "100251560595"}},
    {39, {23, "0b304f2a79a027270276533fe1ed4eff30910876", "146971536592"}},
    {40, {20, "95a156cd21b4a69de969eb6716864f4c8b82a82a", "323724968937"}},
    {41, {25, "d1562eb37357f9e6fc41cb2359f4d3eda4032329", "1003651412950"}},
    {42, {24, "8efb85f9c5b5db2d55973a04128dc7510075ae23", "1458252205147"}},
    {43, {19, "f92044c7924e5525c61207972c253c9fc9f086f7", "2895374552463"}},
    {44, {24, "80df54e1f612f2fc5bdc05c9d21a83aa8d20791e", "7409811047825"}},
    {45, {21, "f0225bfc68a6e17e87cd8b5e60ae3be18f120753", "15404761757071"}},
    {46, {24, "9a012260d01c5113df66c8a8438c9f7a1e3d5dac", "19996463086597"}},
    {47, {27, "f828005d41b0f4fed4c8dca3b06011072cfb07d4", "51408670348612"}},
    {48, {21, "8661cb56d9df0a61f01328b55af7e56a3fe7a2b2", "119666659114170"}},
    {49, {30, "0d2f533966c6578e1111978ca698f8add7fffdf3", "191206974700443"}},
    {50, {29, "de081b76f840e462fa2cdf360173dfaf4a976a47", "409118905032525"}},
    {51, {25, "ef6419cffd7fad7027994354eb8efae223c2dbe7", "611140496167764"}},
    {52, {27, "36af659edbe94453f6344e920d143f1778653ae7", "2058769515153876"}},
    {53, {26, "2f4870ef54fa4b048c1365d42594cc7d3d269551", "4216495639600700"}},
    {54, {30, "cb66763cf7fde659869ae7f06884d9a0f879a092", "6763683971478124"}},
    {55, {31, "db53d9bbd1f3a83b094eeca7dd970bd85b492fa2", "9974455244496707"}},
    {56, {31, "48214c5969ae9f43f75070cea1e2cb41d5bdcccd", "30045390491869460"}},
    {57, {33, "328660ef43f66abe2653fa178452a5dfc594c2a1", "44218742292676575"}},
    {58, {28, "8c2a6071f89c90c4dab5ab295d7729d1b54ea60f", "138245758910846492"}},
    {59, {30, "b14ed3146f5b2c9bde1703deae9ef33af8110210", "199976667976342049"}},
    {60, {31, "cdf8e5c7503a9d22642e3ecfc87817672787b9c5", "525070384258266191"}},
    {61, {25, "68133e19b2dfb9034edf9830a200cfdf38c90cbd", "1135041350219496382"}},
    {62, {35, "e26646db84b0602f32b34b5a62ca3cae1f91b779", "1425787542618654982"}},
    {63, {34, "ef58afb697b094423ce90721fbb19a359ef7c50e", "3908372542507822062"}},
    {64, {34, "3ee4133d991f52fdf6a25c9834e0745ac74248a4", "8993229949524469768"}},
    {65, {37, "52e763a7ddc1aa4fa811578c491c1bc7fd570137", "17799667357578236628"}},
    {66, {35, "20d45a6a762535700ce9e0b216e31994335db8a5", "30568377312064202855"}},
    {67, {31, "739437bb3dd6d1983e66629c5f08c70e52769371", "46346217550346335726"}},
    {68, {34, "e0b8a2baee1b77fc703455f39d51477451fc8cfc", "132656943602386256302"}}
};

// Global variables
vector<unsigned char> TARGET_HASH160_RAW(20);
string TARGET_HASH160;
Int BASE_KEY;
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, size_t, int>> results;
atomic<size_t> total_checked(0);
size_t total_combinations = 0;
vector<string> g_threadPrivateKeys;
mutex progress_mutex;

// Performance tracking
atomic<uint64_t> globalComparedCount(0);
atomic<uint64_t> localComparedCount(0);
double globalElapsedTime = 0.0;
double mkeysPerSec = 0.0;
chrono::time_point<chrono::high_resolution_clock> tStart;
atomic<chrono::time_point<chrono::high_resolution_clock>> lastReportTime(chrono::high_resolution_clock::now());

string formatElapsedTime(double seconds) {
    int hrs = static_cast<int>(seconds) / 3600;
    int mins = (static_cast<int>(seconds) % 3600) / 60;
    int secs = static_cast<int>(seconds) % 60;
    ostringstream oss;
    oss << setw(2) << setfill('0') << hrs << ":"
        << setw(2) << setfill('0') << mins << ":"
        << setw(2) << setfill('0') << secs;
    return oss.str();
}

void signalHandler(int signum) {
    stop_event.store(true);
    cout << "\nInterrupt received, shutting down...\n";
}

class CombinationGenerator {
    int n, k;
    vector<int> current;
    
public:
    CombinationGenerator(int n, int k) : n(n), k(k), current(k) {
        for (int i = 0; i < k; ++i) current[i] = i;
    }

    static size_t combinations_count(int n, int k) {
        if (k > n) return 0;
        if (k * 2 > n) k = n - k;
        if (k == 0) return 1;

        size_t result = n;
        for(int i = 2; i <= k; ++i) {
            result *= (n - i + 1);
            result /= i;
        }
        return result;
    }

    const vector<int>& get() const { return current; }
  
    bool next() {
        int i = k - 1;
        while (i >= 0 && current[i] == n - k + i) --i;
        if (i < 0) return false;
      
        ++current[i];
        for (int j = i + 1; j < k; ++j)
            current[j] = current[j-1] + 1;
        return true;
    }
  
    void unrank(size_t rank) {
        if (rank >= combinations_count(n, k)) {
            current.clear();
            return;
        }
        
        current.resize(k);
        size_t remaining_rank = rank;
        int a = n;
        int b = k;
        size_t x = (combinations_count(n, k) - 1) - rank;
        
        for (int i = 0; i < k; i++) {
            a = largest_a_where_comb_a_b_le_x(a, b, x);
            current[i] = (n - 1) - a;
            x -= combinations_count(a, b);
            b--;
        }
    }
  
private:
    int largest_a_where_comb_a_b_le_x(int a, int b, size_t x) const {
        while (a >= b && combinations_count(a, b) > x) {
            a--;
        }
        return a;
    }
};

inline void prepareShaBlock(const uint8_t* dataSrc, size_t dataLen, uint8_t* outBlock) {
    std::fill_n(outBlock, 64, 0);
    std::memcpy(outBlock, dataSrc, dataLen);
    outBlock[dataLen] = 0x80;
    const uint32_t bitLen = (uint32_t)(dataLen * 8);
    outBlock[60] = (uint8_t)((bitLen >> 24) & 0xFF);
    outBlock[61] = (uint8_t)((bitLen >> 16) & 0xFF);
    outBlock[62] = (uint8_t)((bitLen >>  8) & 0xFF);
    outBlock[63] = (uint8_t)( bitLen        & 0xFF);
}

inline void prepareRipemdBlock(const uint8_t* dataSrc, uint8_t* outBlock) {
    std::fill_n(outBlock, 64, 0);
    std::memcpy(outBlock, dataSrc, 32);
    outBlock[32] = 0x80;
    const uint32_t bitLen = 256;
    outBlock[60] = (uint8_t)((bitLen >> 24) & 0xFF);
    outBlock[61] = (uint8_t)((bitLen >> 16) & 0xFF);
    outBlock[62] = (uint8_t)((bitLen >>  8) & 0xFF);
    outBlock[63] = (uint8_t)( bitLen        & 0xFF);
}

static void computeHash160BatchBinSingle(int numKeys,
                                       uint8_t pubKeys[][33],
                                       uint8_t hashResults[][20])
{
    alignas(32) std::array<std::array<uint8_t, 64>, HASH_BATCH_SIZE> shaInputs;
    alignas(32) std::array<std::array<uint8_t, 32>, HASH_BATCH_SIZE> shaOutputs;
    alignas(32) std::array<std::array<uint8_t, 64>, HASH_BATCH_SIZE> ripemdInputs;
    alignas(32) std::array<std::array<uint8_t, 20>, HASH_BATCH_SIZE> ripemdOutputs;

    const size_t totalBatches = (numKeys + (HASH_BATCH_SIZE - 1)) / HASH_BATCH_SIZE;

    for (size_t batch = 0; batch < totalBatches; batch++) {
        const size_t batchCount = std::min<size_t>(HASH_BATCH_SIZE, numKeys - batch * HASH_BATCH_SIZE);

        // Prepare SHA-256 input blocks
        for (size_t i = 0; i < batchCount; i++) {
            prepareShaBlock(pubKeys[batch * HASH_BATCH_SIZE + i], 33, shaInputs[i].data());
        }
        
        if (batchCount < HASH_BATCH_SIZE) {
            static std::array<uint8_t, 64> shaPadding = {};
            prepareShaBlock(pubKeys[0], 33, shaPadding.data());
            for (size_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                std::memcpy(shaInputs[i].data(), shaPadding.data(), 64);
            }
        }

        const uint8_t* inPtr[HASH_BATCH_SIZE];
        uint8_t* outPtr[HASH_BATCH_SIZE];
        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = shaInputs[i].data();
            outPtr[i] = shaOutputs[i].data();
        }

        sha256avx2_8B(inPtr[0], inPtr[1], inPtr[2], inPtr[3],
                      inPtr[4], inPtr[5], inPtr[6], inPtr[7],
                      outPtr[0], outPtr[1], outPtr[2], outPtr[3],
                      outPtr[4], outPtr[5], outPtr[6], outPtr[7]);

        for (size_t i = 0; i < batchCount; i++) {
            prepareRipemdBlock(shaOutputs[i].data(), ripemdInputs[i].data());
        }

        if (batchCount < HASH_BATCH_SIZE) {
            static std::array<uint8_t, 64> ripemdPadding = {};
            prepareRipemdBlock(shaOutputs[0].data(), ripemdPadding.data());
            for (size_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                std::memcpy(ripemdInputs[i].data(), ripemdPadding.data(), 64);
            }
        }

        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = ripemdInputs[i].data();
            outPtr[i] = ripemdOutputs[i].data();
        }

        ripemd160avx2::ripemd160avx2_32(
            (unsigned char*)inPtr[0], (unsigned char*)inPtr[1],
            (unsigned char*)inPtr[2], (unsigned char*)inPtr[3],
            (unsigned char*)inPtr[4], (unsigned char*)inPtr[5],
            (unsigned char*)inPtr[6], (unsigned char*)inPtr[7],
            outPtr[0], outPtr[1], outPtr[2], outPtr[3],
            outPtr[4], outPtr[5], outPtr[6], outPtr[7]
        );

        for (size_t i = 0; i < batchCount; i++) {
            std::memcpy(hashResults[batch * HASH_BATCH_SIZE + i], ripemdOutputs[i].data(), 20);
        }
    }
}

void worker(Secp256K1* secp, int bit_length, int flip_count, int threadId,
           size_t start, size_t end, mt19937_64& rng) {
    const int fullBatchSize = 2 * POINTS_BATCH_SIZE;
    uint8_t localPubKeys[HASH_BATCH_SIZE][33];
    uint8_t localHashResults[HASH_BATCH_SIZE][20];
    int pointIndices[HASH_BATCH_SIZE];
    
    // Precompute points
    vector<Point> plusPoints(POINTS_BATCH_SIZE);
    vector<Point> minusPoints(POINTS_BATCH_SIZE);
    for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
        Int tmp; tmp.SetInt32(i);
        plusPoints[i] = secp->ComputePublicKey(&tmp);
        minusPoints[i] = plusPoints[i];
        minusPoints[i].y.ModNeg();
    }

    // Structure of Arrays
    vector<Int> deltaX(POINTS_BATCH_SIZE);
    IntGroup modGroup(POINTS_BATCH_SIZE);
    vector<Int> pointBatchX(fullBatchSize);
    vector<Int> pointBatchY(fullBatchSize);

    CombinationGenerator gen(bit_length, flip_count);
    
    // Create and shuffle indices within thread's range
    vector<size_t> indices;
    indices.reserve(end - start);
    
    for(size_t i = start; i < end; i++) {
        indices.push_back(i);
    }
    
    // Fisher-Yates shuffle only within this thread's range
    for(size_t i = indices.size() - 1; i > 0; --i) {
        size_t j = rng() % (i + 1);
        swap(indices[i], indices[j]);
    }

    size_t processed = 0;
    for (size_t rank : indices) {
        if(stop_event.load()) break;
        
        gen.unrank(rank);
        Int currentKey;
        currentKey.Set(&BASE_KEY);
        
        const vector<int>& flips = gen.get();
        
        // Apply flips
        for (int pos : flips) {
            Int mask;
            mask.SetInt32(1);
            mask.ShiftL(pos);
            
            Int temp;
            temp.Set(&currentKey);
            temp.ShiftR(pos);
            
            if (temp.IsEven()) {
                currentKey.Add(&mask);
            } else {
                currentKey.Sub(&mask);
            }
        }

        // Verify key length
        string keyStr = currentKey.GetBase16();
        keyStr = string(64 - keyStr.length(), '0') + keyStr;

        #pragma omp critical
        {
            g_threadPrivateKeys[threadId] = keyStr;
        }

        // Compute public key and process in batches
        Point startPoint = secp->ComputePublicKey(&currentKey);
        Int startPointX, startPointY, startPointXNeg;
        startPointX.Set(&startPoint.x);
        startPointY.Set(&startPoint.y);
        startPointXNeg.Set(&startPointX);
        startPointXNeg.ModNeg();

        // Compute deltaX values in batches of 4 (optimized)
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            deltaX[i].ModSub(&plusPoints[i].x, &startPointX);
            deltaX[i+1].ModSub(&plusPoints[i+1].x, &startPointX);
            deltaX[i+2].ModSub(&plusPoints[i+2].x, &startPointX);
            deltaX[i+3].ModSub(&plusPoints[i+3].x, &startPointX);
        }
        modGroup.Set(deltaX.data());
        modGroup.ModInv();

        // Process plus and minus points in batches
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            for (int j = 0; j < 4; j++) {
                // Plus points (0..255)
                Int deltaY; deltaY.ModSub(&plusPoints[i+j].y, &startPointY);
                Int slope; slope.ModMulK1(&deltaY, &deltaX[i+j]);
                Int slopeSq; slopeSq.ModSquareK1(&slope);
                
                pointBatchX[i+j].Set(&startPointXNeg);
                pointBatchX[i+j].ModAdd(&slopeSq);
                pointBatchX[i+j].ModSub(&plusPoints[i+j].x);
                
                Int diffX; diffX.ModSub(&startPointX, &pointBatchX[i+j]);
                diffX.ModMulK1(&slope);
                
                pointBatchY[i+j].Set(&startPointY);
                pointBatchY[i+j].ModNeg();
                pointBatchY[i+j].ModAdd(&diffX);

                // Minus points (256..511)
                deltaY.ModSub(&minusPoints[i+j].y, &startPointY);
                slope.ModMulK1(&deltaY, &deltaX[i+j]);
                slopeSq.ModSquareK1(&slope);
                
                pointBatchX[POINTS_BATCH_SIZE+i+j].Set(&startPointXNeg);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModAdd(&slopeSq);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModSub(&minusPoints[i+j].x);
                
                diffX.ModSub(&startPointX, &pointBatchX[POINTS_BATCH_SIZE+i+j]);
                diffX.ModMulK1(&slope);
                
                pointBatchY[POINTS_BATCH_SIZE+i+j].Set(&startPointY);
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModNeg();
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModAdd(&diffX);
            }
        }

        // Process keys in optimized batches
        int localBatchCount = 0;
        for (int i = 0; i < fullBatchSize && localBatchCount < HASH_BATCH_SIZE; i++) {
            Point tempPoint;
            tempPoint.x.Set(&pointBatchX[i]);
            tempPoint.y.Set(&pointBatchY[i]);
            
            // Convert to compressed public key
            localPubKeys[localBatchCount][0] = tempPoint.y.IsEven() ? 0x02 : 0x03;
            for (int j = 0; j < 32; j++) {
                localPubKeys[localBatchCount][1 + j] = pointBatchX[i].GetByte(31 - j);
            }
            pointIndices[localBatchCount] = i;
            localBatchCount++;

            if (localBatchCount == HASH_BATCH_SIZE) {
                computeHash160BatchBinSingle(localBatchCount, localPubKeys, localHashResults);
                
                localComparedCount += HASH_BATCH_SIZE;
                
                __m256i target = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(TARGET_HASH160_RAW.data()));

                for (int j = 0; j < HASH_BATCH_SIZE; j++) {
                    __m256i result = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(localHashResults[j]));
                    
                    int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(result, target));
                    
                    const int HASH160_MASK = (1 << 20) - 1;
                    
                    if ((mask & HASH160_MASK) == HASH160_MASK) {
                        bool fullMatch = true;
                        for (int k = 0; k < 20; k++) {
                            if (localHashResults[j][k] != TARGET_HASH160_RAW[k]) {
                                fullMatch = false;
                                break;
                            }
                        }
                        
                        if (fullMatch) {
                            auto tEndTime = chrono::high_resolution_clock::now();
                            globalElapsedTime = chrono::duration<double>(tEndTime - tStart).count();
                            mkeysPerSec = (double)(globalComparedCount + localComparedCount) / globalElapsedTime / 1e6;
                            
                            Int foundKey;
                            foundKey.Set(&currentKey);
                            int idx = pointIndices[j];
                            if (idx < POINTS_BATCH_SIZE) {
                                Int offset; offset.SetInt32(idx);
                                foundKey.Add(&offset);
                            } else {
                                Int offset; offset.SetInt32(idx - POINTS_BATCH_SIZE);
                                foundKey.Sub(&offset);
                            }
                            
                            string hexKey = foundKey.GetBase16();
                            hexKey = string(64 - hexKey.length(), '0') + hexKey;
                            
                            lock_guard<mutex> lock(result_mutex);
                            results.push(make_tuple(hexKey, total_checked.load(), flip_count));
                            stop_event.store(true);
                            return;
                        }
                    }
                }
                
                // Count this as one combination checked
                processed++;
                if(processed % 1000 == 0) {
                    total_checked += 1000;
                }
                localBatchCount = 0;

                // Progress reporting - once per second
                auto now = chrono::high_resolution_clock::now();
                if (chrono::duration<double>(now - lastReportTime.load()).count() >= 1.0 ||
                    processed == indices.size()) {
                    
                    globalElapsedTime = chrono::duration<double>(now - tStart).count();
                    globalComparedCount += localComparedCount;
                    localComparedCount = 0;
                    mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
                    
                    double progress = min(100.0, (double)total_checked / total_combinations * 100.0);
                    
                    lock_guard<mutex> lock(progress_mutex);
                    moveCursorTo(0, 9);
                    
                    cout << "Progress: " << fixed << setprecision(6) << progress << "%\n";
                    cout << "Processed: " << total_checked << " / " << total_combinations << "\n";
                    cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
                    cout << "Elapsed Time: " << formatElapsedTime(globalElapsedTime) << "\n";
                    cout.flush();
                    
                    lastReportTime.store(now);
                }
            }
        }
    }
    total_checked += (processed % 1000);
}

void printUsage(const char* programName) {
    cout << "Usage: " << programName << " [options]\n";
    cout << "Options:\n";
    cout << "  -p, --puzzle NUM    Puzzle number to solve (default: 20)\n";
    cout << "  -t, --threads NUM   Number of CPU cores to use (default: all)\n";
    cout << "  -f, --flips NUM     Override default flip count for puzzle\n";
    cout << "  -h, --help          Show this help message\n";
    cout << "\nExample:\n";
    cout << "  " << programName << " -p 38 -t 8 -f 21\n";
}

int main(int argc, char* argv[]) {
    signal(SIGINT, signalHandler);
    initConsole();
    
    // Parse command line arguments
    int opt;
    int option_index = 0;
    static struct option long_options[] = {
        {"puzzle", required_argument, 0, 'p'},
        {"threads", required_argument, 0, 't'},
        {"flips", required_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    while ((opt = getopt_long(argc, argv, "p:t:f:h", long_options, &option_index)) != -1) {
        if (opt == -1) break;

        switch (opt) {
            case 'p':
                PUZZLE_NUM = atoi(optarg);
                if (PUZZLE_NUM < 20 || PUZZLE_NUM > 68) {
                    cerr << "Error: Puzzle number must be between 20 and 68\n";
                    return 1;
                }
                break;
            case 't':
                WORKERS = atoi(optarg);
                if (WORKERS < 1) {
                    cerr << "Error: Thread count must be at least 1\n";
                    return 1;
                }
                break;
            case 'f':
                FLIP_COUNT = atoi(optarg);
                if (FLIP_COUNT < 1) {
                    cerr << "Error: Flip count must be at least 1\n";
                    return 1;
                }
                break;
            case 'h':
                printUsage(argv[0]);
                return 0;
            default:
                printUsage(argv[0]);
                return 1;
        }
    }

    // Initialize timing at the very start
    tStart = chrono::high_resolution_clock::now();

    Secp256K1 secp;
    secp.Init();
    
    auto puzzle_it = PUZZLE_DATA.find(PUZZLE_NUM);
    if (puzzle_it == PUZZLE_DATA.end()) {
        cerr << "Error: Invalid puzzle number\n";
        return 1;
    }
    
    auto [DEFAULT_FLIP_COUNT, TARGET_HASH160_HEX, PRIVATE_KEY_DECIMAL] = puzzle_it->second;
    
    // Use override flip count if provided, otherwise use puzzle default
    if (FLIP_COUNT == -1) {
        FLIP_COUNT = DEFAULT_FLIP_COUNT;
    }
    
    TARGET_HASH160 = TARGET_HASH160_HEX;
    
    // Convert target hash to bytes
    for (size_t i = 0; i < 20; i++) {
        TARGET_HASH160_RAW[i] = stoul(TARGET_HASH160.substr(i * 2, 2), nullptr, 16);
    }
    
    // Set base key from decimal string
    BASE_KEY.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
    
    // Verify base key
    Int testKey;
    testKey.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
    if (!testKey.IsEqual(&BASE_KEY)) {
        cerr << "Base key initialization failed!\n";
        return 1;
    }

    if (BASE_KEY.GetBitLength() > PUZZLE_NUM) {
        cerr << "Base key exceeds puzzle bit length!\n";
        return 1;
    }
    
    // Calculate total combinations
    total_combinations = CombinationGenerator::combinations_count(PUZZLE_NUM, FLIP_COUNT);
    
    // Format base key for display
    string paddedKey = BASE_KEY.GetBase16();
    size_t firstNonZero = paddedKey.find_first_not_of('0');
    paddedKey = paddedKey.substr(firstNonZero);
    // Add 0x prefix
    paddedKey = "0x" + paddedKey;

    clearTerminal();
    // Print initial header
    cout << "=======================================\n";
    cout << "== Mutagen Puzzle Solver by Denevron ==\n";
    cout << "=======================================\n";    
    cout << "Starting puzzle: " << PUZZLE_NUM << " (" << PUZZLE_NUM << "-bit)\n";
    cout << "Target HASH160: " << TARGET_HASH160.substr(0, 10) << "..." << TARGET_HASH160.substr(TARGET_HASH160.length()-10) << "\n";
    cout << "Base Key: " << paddedKey << "\n";
    cout << "Flip count: " << FLIP_COUNT << " ";
    if (FLIP_COUNT != DEFAULT_FLIP_COUNT) {
        cout << "(override, default was " << DEFAULT_FLIP_COUNT << ")";
    }
    cout << "\n";
    cout << "Total combinations: " << total_combinations << "\n";
    cout << "Using: " << WORKERS << " threads\n";
    g_threadPrivateKeys.resize(WORKERS, "0");
    vector<thread> threads;
    
    // Initialize random number generators for each thread with different seeds
    vector<mt19937_64> rngs(WORKERS);
    random_device rd;
    for (int i = 0; i < WORKERS; i++) {
        rngs[i].seed(rd() + i); // Different seed for each thread
    }
    
    // Distribute work with precise ranges
    size_t comb_per_thread = total_combinations / WORKERS;
    size_t remainder = total_combinations % WORKERS;

    vector<size_t> thread_starts(WORKERS+1);
    for(int i = 0; i < WORKERS; i++) {
        thread_starts[i] = i * comb_per_thread + min((size_t)i, remainder);
    }
    thread_starts[WORKERS] = total_combinations;

    // Launch threads
    for(int i = 0; i < WORKERS; i++) {
        threads.emplace_back(worker, &secp, PUZZLE_NUM, FLIP_COUNT, i,
                           thread_starts[i], thread_starts[i+1], ref(rngs[i]));
    }
    
    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }
    
    if (!results.empty()) {
        auto [hex_key, checked, flips] = results.front();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;

        string compactHex = hex_key;
        size_t firstNonZero = compactHex.find_first_not_of('0');
        compactHex = "0x" + compactHex.substr(firstNonZero);

        cout << "=======================================\n";
        cout << "=========== SOLUTION FOUND ============\n";
        cout << "=======================================\n";
        cout << "Private key: " << compactHex << "\n";
        cout << "Checked " << checked << " combinations\n";
        cout << "Bit flips: " << flips << endl;
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
        
        // Save solution
        ofstream out("puzzle_" + to_string(PUZZLE_NUM) + "_solution.txt");
        if (out) {
            out << hex_key;
            out.close();
            cout << "Solution saved to puzzle_" << PUZZLE_NUM << "_solution.txt\n";
        } else {
            cerr << "Failed to save solution to file!\n";
        }
    } else {
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
        
        cout << "\n\nNo solution found after checking all " << total_combinations << " combinations\n";
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
        
        // Verify puzzle parameters
        cout << "\nVerification:\n";
        cout << "Puzzle: " << PUZZLE_NUM << "\n";
        cout << "Base Key: " << paddedKey << "\n";
        cout << "Target Hash160: " << TARGET_HASH160 << "\n";
        cout << "Flip Count: " << FLIP_COUNT << "\n";
    }
    
    return 0;
}

Tested on low puzzles.
For puzzle 68 code still fails (out of memory).

speed per core?
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 05:11:35 PM
 #8464

speed is same as original code but this is random

got you, but as you say is crashing with high puzzle
Denevron
Newbie
*
Offline Offline

Activity: 121
Merit: 0


View Profile
April 01, 2025, 05:52:35 PM
 #8465

speed is same as original code but this is random

got you, but as you say is crashing with high puzzle

runs now on 68 but only 25% of speed.. useless

Code:
#include <iostream>
#include <array>
#include <iomanip>
#include <fstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <mutex>
#include <random>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <tuple>
#include <immintrin.h>
#include <omp.h>
#include <csignal>
#include <getopt.h>
#include <cstring>

#ifdef _WIN32
    #include <windows.h>
#endif

// Include the required headers
#include "sha256_avx2.h"
#include "ripemd160_avx2.h"
#include "SECP256K1.h"
#include "Point.h"
#include "Int.h"
#include "IntGroup.h"

using namespace std;

// Cross-platform terminal functions
void initConsole() {
#ifdef _WIN32
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD mode = 0;
    GetConsoleMode(hConsole, &mode);
    SetConsoleMode(hConsole, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#endif
}

void clearTerminal() {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[2J\033[H";
#endif
    std::cout.flush();
}

void moveCursorTo(int x, int y) {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {(SHORT)x, (SHORT)y};
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[" << y << ";" << x << "H";
#endif
    std::cout.flush();
}

// Configuration
int PUZZLE_NUM = 20;
int WORKERS = omp_get_num_procs();
int FLIP_COUNT = -1;
static constexpr int POINTS_BATCH_SIZE = 256;
static constexpr int HASH_BATCH_SIZE = 8;

// Puzzle data
const unordered_map<int, tuple<int, string, string>> PUZZLE_DATA = {
    {20, {8, "b907c3a2a3b27789dfb509b730dd47703c272868", "357535"}},
    {21, {9, "29a78213caa9eea824acf08022ab9dfc83414f56", "863317"}},
    {22, {11, "7ff45303774ef7a52fffd8011981034b258cb86b", "1811764"}},
    {23, {12, "d0a79df189fe1ad5c306cc70497b358415da579e", "3007503"}},
    {24, {9, "0959e80121f36aea13b3bad361c15dac26189e2f", "5598802"}},
    {25, {12, "2f396b29b27324300d0c59b17c3abc1835bd3dbb", "14428676"}},
    {26, {14, "bfebb73562d4541b32a02ba664d140b5a574792f", "33185509"}},
    {27, {13, "0c7aaf6caa7e5424b63d317f0f8f1f9fa40d5560", "54538862"}},
    {28, {16, "1306b9e4ff56513a476841bac7ba48d69516b1da", "111949941"}},
    {29, {18, "5a416cc9148f4a377b672c8ae5d3287adaafadec", "227634408"}},
    {30, {16, "d39c4704664e1deb76c9331e637564c257d68a08", "400708894"}},
    {31, {13, "d805f6f251f7479ebd853b3d0f4b9b2656d92f1d", "1033162084"}},
    {32, {14, "9e42601eeaedc244e15f17375adb0e2cd08efdc9", "2102388551"}},
    {33, {15, "4e15e5189752d1eaf444dfd6bff399feb0443977", "3093472814"}},
    {34, {16, "f6d67d7983bf70450f295c9cb828daab265f1bfa", "7137437912"}},
    {35, {19, "f6d8ce225ffbdecec170f8298c3fc28ae686df25", "14133072157"}},
    {36, {14, "74b1e012be1521e5d8d75e745a26ced845ea3d37", "20112871792"}},
    {37, {23, "28c30fb11ed1da72e7c4f89c0164756e8a021d", "42387769980"}},
    {38, {21, "b190e2d40cfdeee2cee072954a2be89e7ba39364", "100251560595"}},
    {39, {23, "0b304f2a79a027270276533fe1ed4eff30910876", "146971536592"}},
    {40, {20, "95a156cd21b4a69de969eb6716864f4c8b82a82a", "323724968937"}},
    {41, {25, "d1562eb37357f9e6fc41cb2359f4d3eda4032329", "1003651412950"}},
    {42, {24, "8efb85f9c5b5db2d55973a04128dc7510075ae23", "1458252205147"}},
    {43, {19, "f92044c7924e5525c61207972c253c9fc9f086f7", "2895374552463"}},
    {44, {24, "80df54e1f612f2fc5bdc05c9d21a83aa8d20791e", "7409811047825"}},
    {45, {21, "f0225bfc68a6e17e87cd8b5e60ae3be18f120753", "15404761757071"}},
    {46, {24, "9a012260d01c5113df66c8a8438c9f7a1e3d5dac", "19996463086597"}},
    {47, {27, "f828005d41b0f4fed4c8dca3b06011072cfb07d4", "51408670348612"}},
    {48, {21, "8661cb56d9df0a61f01328b55af7e56a3fe7a2b2", "119666659114170"}},
    {49, {30, "0d2f533966c6578e1111978ca698f8add7fffdf3", "191206974700443"}},
    {50, {29, "de081b76f840e462fa2cdf360173dfaf4a976a47", "409118905032525"}},
    {51, {25, "ef6419cffd7fad7027994354eb8efae223c2dbe7", "611140496167764"}},
    {52, {27, "36af659edbe94453f6344e920d143f1778653ae7", "2058769515153876"}},
    {53, {26, "2f4870ef54fa4b048c1365d42594cc7d3d269551", "4216495639600700"}},
    {54, {30, "cb66763cf7fde659869ae7f06884d9a0f879a092", "6763683971478124"}},
    {55, {31, "db53d9bbd1f3a83b094eeca7dd970bd85b492fa2", "9974455244496707"}},
    {56, {31, "48214c5969ae9f43f75070cea1e2cb41d5bdcccd", "30045390491869460"}},
    {57, {33, "328660ef43f66abe2653fa178452a5dfc594c2a1", "44218742292676575"}},
    {58, {28, "8c2a6071f89c90c4dab5ab295d7729d1b54ea60f", "138245758910846492"}},
    {59, {30, "b14ed3146f5b2c9bde1703deae9ef33af8110210", "199976667976342049"}},
    {60, {31, "cdf8e5c7503a9d22642e3ecfc87817672787b9c5", "525070384258266191"}},
    {61, {25, "68133e19b2dfb9034edf9830a200cfdf38c90cbd", "1135041350219496382"}},
    {62, {35, "e26646db84b0602f32b34b5a62ca3cae1f91b779", "1425787542618654982"}},
    {63, {34, "ef58afb697b094423ce90721fbb19a359ef7c50e", "3908372542507822062"}},
    {64, {34, "3ee4133d991f52fdf6a25c9834e0745ac74248a4", "8993229949524469768"}},
    {65, {37, "52e763a7ddc1aa4fa811578c491c1bc7fd570137", "17799667357578236628"}},
    {66, {35, "20d45a6a762535700ce9e0b216e31994335db8a5", "30568377312064202855"}},
    {67, {31, "739437bb3dd6d1983e66629c5f08c70e52769371", "46346217550346335726"}},
    {68, {34, "e0b8a2baee1b77fc703455f39d51477451fc8cfc", "132656943602386256302"}}
};

// Global variables
vector<unsigned char> TARGET_HASH160_RAW(20);
string TARGET_HASH160;
Int BASE_KEY;
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, size_t, int>> results;
atomic<size_t> total_checked(0);
size_t total_combinations = 0;
vector<string> g_threadPrivateKeys;
mutex progress_mutex;

// Performance tracking
atomic<uint64_t> globalComparedCount(0);
double globalElapsedTime = 0.0;
double mkeysPerSec = 0.0;
chrono::time_point<chrono::high_resolution_clock> tStart;
atomic<chrono::time_point<chrono::high_resolution_clock>> lastReportTime(chrono::high_resolution_clock::now());

string formatElapsedTime(double seconds) {
    int hrs = static_cast<int>(seconds) / 3600;
    int mins = (static_cast<int>(seconds) % 3600) / 60;
    int secs = static_cast<int>(seconds) % 60;
    ostringstream oss;
    oss << setw(2) << setfill('0') << hrs << ":"
        << setw(2) << setfill('0') << mins << ":"
        << setw(2) << setfill('0') << secs;
    return oss.str();
}

void signalHandler(int signum) {
    stop_event.store(true);
    cout << "\nInterrupt received, shutting down...\n";
}

class CombinationGenerator {
    int n, k;
    vector<int> current;
   
public:
    CombinationGenerator(int n, int k) : n(n), k(k), current(k) {
        for (int i = 0; i < k; ++i) current[i] = i;
    }

    static size_t combinations_count(int n, int k) {
        if (k > n) return 0;
        if (k * 2 > n) k = n - k;
        if (k == 0) return 1;

        size_t result = n;
        for(int i = 2; i <= k; ++i) {
            result *= (n - i + 1);
            result /= i;
        }
        return result;
    }

    const vector<int>& get() const { return current; }
 
    void unrank(size_t rank) {
        if (rank >= combinations_count(n, k)) {
            current.clear();
            return;
        }
       
        current.resize(k);
        size_t remaining_rank = rank;
        int a = n;
        int b = k;
        size_t x = (combinations_count(n, k) - 1) - rank;
       
        for (int i = 0; i < k; i++) {
            a = largest_a_where_comb_a_b_le_x(a, b, x);
            current[i] = (n - 1) - a;
            x -= combinations_count(a, b);
            b--;
        }
    }
 
private:
    int largest_a_where_comb_a_b_le_x(int a, int b, size_t x) const {
        while (a >= b && combinations_count(a, b) > x) {
            a--;
        }
        return a;
    }
};

void computeHash160BatchBinSingle(int numKeys, uint8_t pubKeys[][33], uint8_t hashResults[][20]) {
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> shaInputs;
    alignas(32) array<array<uint8_t, 32>, HASH_BATCH_SIZE> shaOutputs;
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> ripemdInputs;
    alignas(32) array<array<uint8_t, 20>, HASH_BATCH_SIZE> ripemdOutputs;

    const size_t totalBatches = (numKeys + (HASH_BATCH_SIZE - 1)) / HASH_BATCH_SIZE;

    for (size_t batch = 0; batch < totalBatches; batch++) {
        const size_t batchCount = min<size_t>(HASH_BATCH_SIZE, numKeys - batch * HASH_BATCH_SIZE);

        // Prepare SHA-256 input blocks
        for (size_t i = 0; i < batchCount; i++) {
            memset(shaInputs[i].data(), 0, 64);
            memcpy(shaInputs[i].data(), pubKeys[batch * HASH_BATCH_SIZE + i], 33);
            shaInputs[i][33] = 0x80;
            shaInputs[i][60] = 0x01;
            shaInputs[i][61] = 0x00;
            shaInputs[i][62] = 0x00;
            shaInputs[i][63] = 0xA0;
        }

        // Process SHA-256
        sha256avx2_8B(shaInputs[0].data(), shaInputs[1].data(), shaInputs[2].data(), shaInputs[3].data(),
                     shaInputs[4].data(), shaInputs[5].data(), shaInputs[6].data(), shaInputs[7].data(),
                     shaOutputs[0].data(), shaOutputs[1].data(), shaOutputs[2].data(), shaOutputs[3].data(),
                     shaOutputs[4].data(), shaOutputs[5].data(), shaOutputs[6].data(), shaOutputs[7].data());

        // Prepare RIPEMD-160 input blocks
        for (size_t i = 0; i < batchCount; i++) {
            memset(ripemdInputs[i].data(), 0, 64);
            memcpy(ripemdInputs[i].data(), shaOutputs[i].data(), 32);
            ripemdInputs[i][32] = 0x80;
            ripemdInputs[i][60] = 0x00;
            ripemdInputs[i][61] = 0x01;
            ripemdInputs[i][62] = 0x00;
            ripemdInputs[i][63] = 0x00;
        }

        // Process RIPEMD-160
        ripemd160avx2::ripemd160avx2_32(
            ripemdInputs[0].data(), ripemdInputs[1].data(),
            ripemdInputs[2].data(), ripemdInputs[3].data(),
            ripemdInputs[4].data(), ripemdInputs[5].data(),
            ripemdInputs[6].data(), ripemdInputs[7].data(),
            ripemdOutputs[0].data(), ripemdOutputs[1].data(),
            ripemdOutputs[2].data(), ripemdOutputs[3].data(),
            ripemdOutputs[4].data(), ripemdOutputs[5].data(),
            ripemdOutputs[6].data(), ripemdOutputs[7].data()
        );

        // Copy results
        for (size_t i = 0; i < batchCount; i++) {
            memcpy(hashResults[batch * HASH_BATCH_SIZE + i], ripemdOutputs[i].data(), 20);
        }
    }
}

void worker_large(Secp256K1* secp, int bit_length, int flip_count, int threadId,
                 uint64_t start_range, uint64_t end_range, mt19937_64& rng) {
    uint8_t localPubKeys[HASH_BATCH_SIZE][33];
    uint8_t localHashResults[HASH_BATCH_SIZE][20];
    int pointIndices[HASH_BATCH_SIZE];
   
    // Precompute points
    vector<Point> plusPoints(POINTS_BATCH_SIZE);
    vector<Point> minusPoints(POINTS_BATCH_SIZE);
    for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
        Int tmp; tmp.SetInt32(i);
        plusPoints[i] = secp->ComputePublicKey(&tmp);
        minusPoints[i] = plusPoints[i];
        minusPoints[i].y.ModNeg();
    }

    // Structure of Arrays
    vector<Int> deltaX(POINTS_BATCH_SIZE);
    IntGroup modGroup(POINTS_BATCH_SIZE);
    vector<Int> pointBatchX(2 * POINTS_BATCH_SIZE);
    vector<Int> pointBatchY(2 * POINTS_BATCH_SIZE);

    CombinationGenerator gen(bit_length, flip_count);
    size_t processed = 0;
    const size_t report_interval = 1000000;

    while (!stop_event.load()) {
        // Generate random rank within thread's range
        uint64_t rank = start_range + (rng() % (end_range - start_range));
       
        gen.unrank(rank);
        Int currentKey;
        currentKey.Set(&BASE_KEY);
       
        const vector<int>& flips = gen.get();
       
        // Apply flips
        for (int pos : flips) {
            Int mask; mask.SetInt32(1); mask.ShiftL(pos);
            Int temp; temp.Set(&currentKey); temp.ShiftR(pos);
            temp.IsEven() ? currentKey.Add(&mask) : currentKey.Sub(&mask);
        }

        // Compute public key
        Point startPoint = secp->ComputePublicKey(&currentKey);
        Int startPointX, startPointY, startPointXNeg;
        startPointX.Set(&startPoint.x);
        startPointY.Set(&startPoint.y);
        startPointXNeg.Set(&startPointX);
        startPointXNeg.ModNeg();

        // Process points in batches
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            deltaX[i].ModSub(&plusPoints[i].x, &startPointX);
            deltaX[i+1].ModSub(&plusPoints[i+1].x, &startPointX);
            deltaX[i+2].ModSub(&plusPoints[i+2].x, &startPointX);
            deltaX[i+3].ModSub(&plusPoints[i+3].x, &startPointX);
        }
        modGroup.Set(deltaX.data());
        modGroup.ModInv();

        // Process keys in optimized batches
        int localBatchCount = 0;
        for (int i = 0; i < 2 * POINTS_BATCH_SIZE && localBatchCount < HASH_BATCH_SIZE; i++) {
            Point tempPoint;
            if (i < POINTS_BATCH_SIZE) {
                tempPoint.x.Set(&plusPoints[i].x);
                tempPoint.y.Set(&plusPoints[i].y);
            } else {
                tempPoint.x.Set(&minusPoints[i-POINTS_BATCH_SIZE].x);
                tempPoint.y.Set(&minusPoints[i-POINTS_BATCH_SIZE].y);
            }
           
            localPubKeys[localBatchCount][0] = tempPoint.y.IsEven() ? 0x02 : 0x03;
            for (int j = 0; j < 32; j++) {
                localPubKeys[localBatchCount][1 + j] = tempPoint.x.GetByte(31 - j);
            }
            pointIndices[localBatchCount] = i;
            localBatchCount++;

            if (localBatchCount == HASH_BATCH_SIZE) {
                computeHash160BatchBinSingle(localBatchCount, localPubKeys, localHashResults);
               
                // Check for matches
                for (int j = 0; j < HASH_BATCH_SIZE; j++) {
                    if (memcmp(localHashResults[j], TARGET_HASH160_RAW.data(), 20) == 0) {
                        Int foundKey;
                        foundKey.Set(&currentKey);
                        int idx = pointIndices[j];
                        if (idx < POINTS_BATCH_SIZE) {
                            Int offset; offset.SetInt32(idx);
                            foundKey.Add(&offset);
                        } else {
                            Int offset; offset.SetInt32(idx - POINTS_BATCH_SIZE);
                            foundKey.Sub(&offset);
                        }
                       
                        string hexKey = foundKey.GetBase16();
                        hexKey = string(64 - hexKey.length(), '0') + hexKey;
                       
                        lock_guard<mutex> lock(result_mutex);
                        results.push(make_tuple(hexKey, total_checked.load(), flip_count));
                        stop_event.store(true);
                        return;
                    }
                }
               
                // Progress reporting
                processed++;
                if (processed % report_interval == 0) {
                    total_checked += report_interval;
                   
                    auto now = chrono::high_resolution_clock::now();
                    if (chrono::duration<double>(now - lastReportTime.load()).count() >= 1.0) {
                        globalElapsedTime = chrono::duration<double>(now - tStart).count();
                        mkeysPerSec = total_checked / globalElapsedTime / 1e6;
                       
                        lock_guard<mutex> lock(progress_mutex);
                        moveCursorTo(0, 9);
                        cout << "Progress: " << fixed << setprecision(6)
                             << (100.0 * total_checked / total_combinations) << "%\n"
                             << "Processed: " << total_checked << "\n"
                             << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n"
                             << "Elapsed: " << formatElapsedTime(globalElapsedTime) << "\n";
                        cout.flush();
                       
                        lastReportTime.store(now);
                    }
                }
                localBatchCount = 0;
            }
        }
    }
}

int main(int argc, char* argv[]) {
    signal(SIGINT, signalHandler);
    initConsole();
   
    // Parse command line arguments
    int opt;
    while ((opt = getopt(argc, argv, "p:t:f:h")) != -1) {
        switch (opt) {
            case 'p': PUZZLE_NUM = atoi(optarg); break;
            case 't': WORKERS = atoi(optarg); break;
            case 'f': FLIP_COUNT = atoi(optarg); break;
            case 'h':
                cout << "Usage: " << argv[0] << " [-p puzzle] [-t threads] [-f flips]\n";
                return 0;
            default:
                cerr << "Invalid option\n";
                return 1;
        }
    }

    // Initialize
    tStart = chrono::high_resolution_clock::now();
    Secp256K1 secp; secp.Init();
   
    auto puzzle_it = PUZZLE_DATA.find(PUZZLE_NUM);
    if (puzzle_it == PUZZLE_DATA.end()) {
        cerr << "Error: Invalid puzzle number\n";
        return 1;
    }
   
    auto [DEFAULT_FLIP_COUNT, TARGET_HASH160_HEX, PRIVATE_KEY_DECIMAL] = puzzle_it->second;
    FLIP_COUNT = (FLIP_COUNT == -1) ? DEFAULT_FLIP_COUNT : FLIP_COUNT;
    TARGET_HASH160 = TARGET_HASH160_HEX;
   
    // Convert target hash to bytes
    for (size_t i = 0; i < 20; i++) {
        TARGET_HASH160_RAW[i] = stoul(TARGET_HASH160.substr(i * 2, 2), nullptr, 16);
    }
   
    // Set base key
    BASE_KEY.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
    total_combinations = CombinationGenerator::combinations_count(PUZZLE_NUM, FLIP_COUNT);

    clearTerminal();
    cout << "=======================================\n"
         << "== Mutagen Puzzle Solver (Optimized) ==\n"
         << "=======================================\n"
         << "Puzzle: " << PUZZLE_NUM << " (" << PUZZLE_NUM << "-bit)\n"
         << "Target: " << TARGET_HASH160.substr(0, 10) << "..." << TARGET_HASH160.substr(30) << "\n"
         << "Flips: " << FLIP_COUNT << "\n"
         << "Threads: " << WORKERS << "\n";

    if (PUZZLE_NUM >= 50) {
        cout << "Using memory-optimized mode for large puzzle\n";
    }

    // Launch workers
    vector<thread> threads;
    vector<mt19937_64> rngs(WORKERS);
    random_device rd;
   
    uint64_t range_per_thread = UINT64_MAX / WORKERS;
    for (int i = 0; i < WORKERS; i++) {
        rngs[i].seed(rd() + i);
        threads.emplace_back(worker_large, &secp, PUZZLE_NUM, FLIP_COUNT, i,
                           i * range_per_thread,
                           (i == WORKERS-1) ? UINT64_MAX : (i+1)*range_per_thread,
                           ref(rngs[i]));
    }
   
    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }

    // Report results
    if (!results.empty()) {
        auto [hex_key, checked, flips] = results.front();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = total_checked / globalElapsedTime / 1e6;

        string compactHex = hex_key;
        size_t firstNonZero = compactHex.find_first_not_of('0');
        compactHex = "0x" + compactHex.substr(firstNonZero);

        cout << "=======================================\n"
             << "=========== SOLUTION FOUND ============\n"
             << "=======================================\n"
             << "Private key: " << compactHex << "\n"
             << "Checked " << checked << " combinations\n"
             << "Bit flips: " << flips << "\n"
             << "Time: " << fixed << setprecision(2) << globalElapsedTime << "s\n"
             << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
       
        ofstream out("solution.txt");
        if (out) out << hex_key;
    } else {
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        cout << "\nNo solution found after " << total_checked << " combinations\n"
             << "Elapsed: " << formatElapsedTime(globalElapsedTime) << "\n";
    }
   
    return 0;
}

If you took it from one of the repositories, it would be easier to modify)
nomachine
Full Member
***
Offline Offline

Activity: 812
Merit: 134



View Profile
April 01, 2025, 06:05:10 PM
 #8466

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

I noticed something, let's say if the speed is 75 million per second, then in theory we should go through 50 billion in 15 minutes, but now it takes more time)

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

First, let’s agree on whether it takes 34, 35, 36, or 37 flips.  Tongue

Number of mutation options for 68 bit of 34: 28453041475240576740
Number of mutation options for 68 bit of 35: 27640097433090845976
Number of mutation options for 68 bit of 36: 25336755980333275478
Number of mutation options for 68 bit of 37: 21912870037044995008


Then you need to correct the script so that it doesn't calculate for Flip count: 34
Total combinations: 47478523248093572  Tongue


He is right.....All calculations were wrong above 64bit...


https://github.com/NoMachine1/Mutagen/commit/b97c178cfe618776784b7e5f23690e2c12311517

need to be
C(68,37) = 21912870037044995008

checked on wolframalpha

This wasn't exactly an easy fix.   Grin

BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
Denevron
Newbie
*
Offline Offline

Activity: 121
Merit: 0


View Profile
April 01, 2025, 06:20:58 PM
 #8467

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

I noticed something, let's say if the speed is 75 million per second, then in theory we should go through 50 billion in 15 minutes, but now it takes more time)

maybe someone will do it on a GPU, and someone will run it on 10-20 cards  Grin

20 GPUs could reach ~40,000 Mkeys/s   Grin

First, let’s agree on whether it takes 34, 35, 36, or 37 flips.  Tongue

Number of mutation options for 68 bit of 34: 28453041475240576740
Number of mutation options for 68 bit of 35: 27640097433090845976
Number of mutation options for 68 bit of 36: 25336755980333275478
Number of mutation options for 68 bit of 37: 21912870037044995008


Then you need to correct the script so that it doesn't calculate for Flip count: 34
Total combinations: 47478523248093572  Tongue


He is right.....All calculations were wrong above 64bit...


https://github.com/NoMachine1/Mutagen/commit/b97c178cfe618776784b7e5f23690e2c12311517

need to be
C(68,37) = 21912870037044995008

checked on wolframalpha

This wasn't exactly an easy fix.   Grin

so there will still be 28453041475240576740 combinations, not 47478523248093572   Grin
nomachine
Full Member
***
Offline Offline

Activity: 812
Merit: 134



View Profile
April 01, 2025, 06:44:49 PM
 #8468

so there will still be 28453041475240576740 combinations, not 47478523248093572   Grin

Yes, if the script uses 128-bit counters and 128-bit arithmetic. After that, the next step is optimizing this version for AVX2.  Grin

BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
GoldTiger69
Hero Member
*****
Offline Offline

Activity: 595
Merit: 502


View Profile WWW
April 01, 2025, 07:10:01 PM
 #8469

Hey guys, looks like Mutagen is grabbing some traction; How about a new thread for it exclusively? Just saying.

Cheers.

I can help you to restore/recover your wallet or password.
https://bitcointalk.org/index.php?topic=1234619.0
kTimesG
Full Member
***
Offline Offline

Activity: 812
Merit: 248


View Profile
April 01, 2025, 07:15:48 PM
 #8470

OK, I have to finally admit it.

I had it wrong with the way I was viewing things. I had some spare time (I don't do fishing yet) so I played around with the possibility that maybe, just maybe, prefixes might indeed be useful to accelerate the brute-force.

I can't believe how ashamed I feel, inside out, after managing to speed up a plain old scan search by 800% on average, by using prefixes as a good hint to stop all the GPU instances currently running and reset all the offsets and ranges based on the new informations.

So now my baby is purring out results at an astonishing speed, check this out. If you don't trust that my cracking grid spits out correct keys, you can check them one by one. I'm gonna let you figure out just how many hashes are usually needed to find a 32-bit minimum prefix match, but boy, look at that bad ass speed!!!



To apologize for my fuck-up, I'm gonna share a few nice results I managed to grab recently, feel free to use them as you wish!

Code:
0xc4cf15366c5d3e412 1MVDYgVaSeWVxvT8vVRkujyHHoboXcUm6 0372222bf4e46bc20bb3d591f4350f4ccc5ae740040f948d0cce00cb5aef1c96ef
0xa59fc4663314c8c13 1MVDYgVaSCGMAW6FeE9jJJbrv2877Qh4C 035a1a8b0f903340a2e80df22b733e81d0f97c4252739879079d68803371cf72b7
0xa7cf01ad011bf227a 1MVDYgVaTTjBZ4ZPCJv4UYWYSt6792kds 025df7a3396982755ee35189402dda5aaf532c5bbd30f6e584486565929390b3e2
0x8f549725db946b1b8 1MVDYgVa89Q7rBWZaXcTf1UVPJ9BwDCX8 02c8befe745333183bb1ec263134f788fb768f6202053568f65c9c1506a17030e4
0x9468c372a5ce48cd5 1MVDYgVaDCnqjuvMjDSfS1nURGMR8cmuf 03d2a7ad2697e57153d31e4437830df14958dbe52b2dd89674c7e9a5cdf44b7763
0xa6e64129243ee31dc 1MVDYgVaCH57TSAQwoWMqX9Ztz8iNkpzW 0284e277b768a2a0e6d32ba9714ec4fe7d626a80d748c8ff457827f021235682d9
0xb1575cd28d49e82fb 1MVDYgVa7ZsJYQ8SFaXMSfoFJJgtgTtRY 02f03c02369c57339aaed65e1fb691ec3e6ff2bc1e80b15dddaf8e017d6f2761cb
0xc4e3650fd1b15ac18 1MVDYgVaHi8Q8rDmFYxkUkRq9vZpx5fE2 02fa68270c520d74351183b369b09001294f3b9796f1dfae78af15c28a70cb0436
0x8c7450c9a20a70e8d 1MVDYgVa3WmZ7XQK9PvzTBp6GvX96vdhy 03c535511fb1bf74b8433a626734446d8e68509e077b5ca08d09eb01060d1fb6ea
0x98ed1dd57d11bdcc4 1MVDYgVa34juD8zJFk88379qUjXju4ekQ 039a9caa2bb588fedcdfb6c44856f4e73d3044101b37f4cda51e48417ae49fe567
0xe7a0def14aea45067 1MVDYgVZvxTgAhh499DjDcG9YRaSdrAqD 036d6132ccf2e0fdef4bf35e595083930ce2e59fbfd04ad45e172c7994ec131f4e
0xe7c83c2c99506fc88 1MVDYgVa2qshGvVjSTi9MwkSHL1iUm2bU 030aa97b1220ee84638da8f4de8a93a77186f9d0cf54c5091aa39108360aed5770
0xea306846e30c25532 1MVDYgVa2soVBDM8U7vz2PS5Bwwsyvrfm 0320f88f4db86ad51678ee34cc7edf4f33f07b6059499d4eecb7636fdcfd7cedb4
0xf68a81d6a691ec69a 1MVDYgVa5UZv1JpqTMg3bi3oMM9CGJ8D2 03fda232afac8f6c9c79e428fbea4f69d390d33565716e3e0c9f2663709ff4fd24
0x9292254947908c82c 1MVDYgVaX8s2T7LS7Z9jHtHp3VW4d8dFS 037f4d46b12147ca48496a3ccba5a4a1984e67c3fefc4076472722412b2ec6a0a7
0x9ec5992003a4dadc6 1MVDYgVaaysSySK4bbDTKDkX5bsF8VojK 03fdf0c21b3272960296190a9ed54d5cf8e610de7fa24b777b2d1896199f25aa3c
0xa1d1d432e5556bab8 1MVDYgVawZkXLui2q5bH5wfPch1c4sdbn 03e7666c3bf6f535006a4d5cd1029eb5056176f80a11ec7b5f3509bde018e85283
0xaae90eb0cccb66062 1MVDYgVafTYPwaMyoratNAKSoxqWHGNGW 02a440bd241721eadfae0adfc39cc5f01a7961496e9911f17d13e7efb1f047f359
0xb45e67b553064938d 1MVDYgVaa6EGxVvwSnpbxQo3tJukg1uvb 03df9139f5820da4f3c23816d51417da7fc7a08d2b041c8931dd05c26348dcca16
0xc1715aa75c9addff4 1MVDYgVaaKfVGv1sSsfszDjwv77ciwK2C 02bacfd7ace256c8fef2f44fe1d9e9d1e0163e4fb5730eec7613213a0e9577e024
0xc5a3ea3ae2fb3f2e0 1MVDYgVaWC9o4SGj9yr4p17CH7QQSQnmR 029b6833144031ed7e27e3e01ac5adb893cf736ed4a47c9c64c00e6edb6c908fbb
0xcc74ef889d8fb4df8 1MVDYgVanb37tvZL7eXLhit7v9TYuB6Qd 034401be05aaaaf37790aa9f0cb28f8fb2c6be86962c9bad286bcd075c130244ab
0xd4b5b95d9059b0b6a 1MVDYgVb4dqSk9horLK28Yp9CLJgsFhyp 03e39346f8ec02ea5d7d1d15eab91b4fcd751c9c8c18b1ba039cf089a7c32a1769
0xdd48d47cad650d43f 1MVDYgVaeGi2w6xMy519XmjzjFBHbu11D 034994afee63f045733415827d62c4ac821b765bd0eaef6a3a446a478d2d1ea013
0xebf74596a5782a3f3 1MVDYgVb985cANsV7MGUtoYmquC6HogNK 022da6acacc2bf7884dca91b1d535a32799c51cdb7935eae5528a796a834000110
0x80f9a22915619799e 1MVDYgVbczqnMaRQXaXPHKYxGaWKR2VSB 027a32f73fd72476a37ad99513a0aa4994244df5dd97ef4307f39e028ed210d973
0x8286daab06e728065 1MVDYgVcVb59KJCp5D7toEQw91xVoZEk7 027e9d74c0230e30154523e586c78463e7269088913f85e0b76e8f8afb571a43cf
0x830a6dc8e7b186ef4 1MVDYgVbttrNR2iepzfYNvSWVXjzKm3ab 039743581b5a91c059d238b12c4f6175e8fffd12b015832ef71a22ca117d31864e
0x856808a6e195d6ee0 1MVDYgVcLfodUcRARmu3MJdGoBNb2G7QP 0205d67026caef7c016079ce06b98ef3e8bc21440445c557cd8c78c605d736be6c
0x8834d28549e657a6b 1MVDYgVbMndK6gonHeSv8KN4hJKZivtFi 02e86eb277b13a0ef3af813b3cfe977bcca40eaa9f9f0194c96b0ee2e8060e3fcb
0x8b8788ee35ab6f688 1MVDYgVceaSaWk4igpMBBDdXAu2NhPmFX 023b418523b3f8f03b4e4fee46a123ed501ee3e09605f37d0f77678e5cf52dc6ff
0x8d6c43786e61ca0f0 1MVDYgVcWwJ4g6Ru5iRSamGrW9RcuRRCP 020239984c4ff3aca947ddfbb8578eb38db6b4ad8c95f244aabeedf9ee354241ca
0x8f165aab5ef06ad88 1MVDYgVbizW2eYrMVF1x31XedFHNkpsy3 03254ef919d0faa4e32f346410c213c34d9aa600b383e7f72b6e71b4a5f370ee28
0x9a113c6c3562cb270 1MVDYgVbH35pfnuxAaw1QMypNfBrAw5h6 03909b672b3ced7a23a57612d4c13e7c054d4b7eaf76d4037c86f8738b07217f05
0x9c499b345e951c47b 1MVDYgVcaXHFuyNfSGCndHyxfnX1FQ7da 02803c366fc2762596dd2d9850d2d0f904eb306c51f7218306905da648d054ee68
0x9cebd9be1e8635286 1MVDYgVbvJfWvuvcHxGJrmUerPhE4SDro 03a24b1b62952342acef9f2a3d441a24e48e2a159bd9c9767f3b48cb9aaeef55a1
0xa082bf833dd4c729d 1MVDYgVbxhHJSWbFPmpRLBARecQkCULXi 03815a9cf5dc7de202cfd8c9ea0c4063f79dfb89e7780e97f0444a96eced6fe74e
0xa3511b31aa3c7f8a5 1MVDYgVcKYEoNovpX53wWsZZ44cKSvEnG 02788bcf6d9ae17b54e2a7eada5f5f9f0addfbf7d5290d11046cfcdbd582d8bb1c
0xa63215a737a6c13e6 1MVDYgVc6KrA1GensbpNG78ruFtFHi3Y4 02add55320bd62949d5ec2996b3d7e6e82227867ee3af5b3759ca4fdb699c16665
0xa7a74438ca8603bc7 1MVDYgVbczLHJFQyjWH2CCpoTkjdg6He8 02ee6d5228700b1d3c436c28c509f42c85a94cef5d1df8ceb4cb0b199046b71614
0xaf2cf75aaf7d2cfe4 1MVDYgVcJKCkCboRWSuNwdPf8jW4qW3Jq 020a249ab92368fb166be1a77415916b4b324238cd2cb8f2a6ea9521dd306036d1
0xb175e5d7b5149348e 1MVDYgVcGruUyjSyH7Ct5c7S9dgLk3dhh 0306a1b8a294dbfb66b8f634623278daf89df43065bfb01c3264cb0dd9ca012360
0xb2054f604e6fe93e5 1MVDYgVcZw3jnuMQVrgBnVr1capW3QLyo 03d61fc6547389a4ba5b2dc5409cd9ae56247b685b932aaf796f72b81f7af27af0
0xb3f858dfe2c3abc32 1MVDYgVbaNnRKk4866xubVCQthZHNMucR 024291bb32c33666a7fbf6a577d67bb26f54b61b39f2134cb8fe23a0cb20029135
0xb66df5467aa2f5c89 1MVDYgVbeuM7fxzwT6DwbBeEetBwZHnay 03755b0ccab1973d2355eae5095a6006e108560b5dd823903dbdfe672f5f66b07c
0xb8108f1c949edf349 1MVDYgVcNS5rwbw6Ae9P87GBrSUwYNe59 034099eb475e157e01fda361460c44879f2042b24605529c46c96d0303fe75808c
0xb958b8feb47fc3344 1MVDYgVc3jQwGFmXRqE8Brcnt162agbeu 03cf85ea9de786c828d46603491d0d10e1e5624224f737b530657ad27e88a5c439
0xbff333ada94e53a57 1MVDYgVbQVwMuZZ7Fk55mzN9aPVv3HkAE 027103fc06daa9a7ba536353506e560275e45c3d211bccc03cec593fd851bd4daf
0xc251183d8a7cca7a5 1MVDYgVbNrnHEejxuN8E4ACPSV9z9QQj2 038ab193e66457c09257838e2af39a5094b7fc6abd5d6e19b31872fe1b597227b5
0xc2e7c6fd21e66e389 1MVDYgVcZfSJkZ6id2mceBkV7cGXkKqTk 032a6113d7b44a15e46a59843023564471768ca676f02d2e7218f8b976d9da40a8
0xc44afc2001582cd79 1MVDYgVbSHGvy6mdtrQDfh2UoQSyYG9oK 02c3e9d2842fdc53c274b12cb4c9bd1ba0960a341adf4ae76fe2641ce55aa5f618
0xccc7129a330cf2b54 1MVDYgVbbAtBJA7bw8DHj7FUToUU22zZ8 032d5866e989321533d1d8a50296199860a5d1e798568f65336d698892f6a25b48
0xd05c6446a36f37ec7 1MVDYgVbgjPUrteB3ifNLLHepREf1Lfye 039536215bb7ee81ae278c369bd8458289c69df948db918e4c81ae2cfd0721fbb5
0xd173007057baea098 1MVDYgVbbhTssLoUCE1AXWPnFXnquoKzE 02f2933969f86a5841d3a7ac42ba5032d9b31d13096b7e1a8235bf92f3778bec76
0xd9b49644827b2a5d0 1MVDYgVbgA1fkKNECFuTjms4ZJv8JZRcu 025bd8fbbd1b6e9fe530f4c33335358a4c1ab07d77c79d9ba75a65923f097ad41a
0xde34dae79f4c7b982 1MVDYgVcKDLrcgB9WSmb4z3ifmDhiSPiP 02d13c2fec303aed19f586246d73c567db1967307ae580fc778e8bb574e879f50f
0xe59da4cd15b851643 1MVDYgVbF5RLm7sRYk6Epeqj2NwwE9g1P 02bd84576f7c4eb9acedc1ba26f47a8d374b2ff09bba18416faab7ef0cfb5721c6
0xe76651e59864e8ffd 1MVDYgVcTnxhgWxmjdzJCr4o2bTFfWEqo 021f269ae7f3bedc8223e2abb2c66defe76f70448d4052039564d3f20e6cd3aa4e
0xedb056f19b3b7e978 1MVDYgVbMKv6FWAoqU1UmRpPMiDYLpTZK 0290b437276ccd7361b79af0219526f892d345db15b91ce2edf4ade83e2b8e71ff
0xeef3d5de8f60848aa 1MVDYgVc7L3DpYSDcNudjdAXXn7fR45He 02584494f02759351078e8b6efd65ef50264d5b363e053aba6fd177a1236bba189
0xef03db9594d46948d 1MVDYgVbdBLfdiEK1mGy6xXPTfauSzTWg 0271eace4829ea972516eb3f4d3c59a2ad78531b994d8f320b8c8c57e5045fbf73
0xf2f5d60eda10e663b 1MVDYgVbNjRsQbLXQBK4o1BeuZtYyPVWH 026fd6bcaea1558618c38865971d088774c74d3eee1130c52f09df8365b7e31217
0xf68a76a1a957e717e 1MVDYgVcLJFT935gCXxMd4qzZUNGLD7BD 03cfabfdcc89b1c242b860e1023154be7c74d40a85bcfbc612e71882219626d266
0xf7aa3c2801c4622fe 1MVDYgVbGn8hAwUqjv9ZoRRjiB32odVmK 03f04749db897868f0257e1196581d58dd04a880f0a22dc7de955d5743c87b2d31
0xf7c1e9abfde5064ed 1MVDYgVc3hZPHSoxi4C7d8iBQZU8HgiAT 02b30a7b233b3630989dfb9dfb112024b182a451622d5daf9e48682c3996fe237c
0xfbce787b31400e3cd 1MVDYgVbgmHjzXLoTunHtBwqNWKTna8PL 03a5ffe4a8df77dd6afe16e5b7314ed33c64e8897538fceacf7a9f571833874586
0xfe9057e046e196507 1MVDYgVcDS1m8CA2YPkNmNZQq9c8t9f1q 03ae326fb4b2ec9dd7c0915b1add7cb179964e2457661f4b17017ad1f2c3b4b93c
0xff420fc4fe6aa1203 1MVDYgVcGpfkqV7iQXDZb6b55XUVunS5w 03f13e24b4e57de088972d4f9cbd3ca20be80c4e501d561278e70fb0e1b2b375b2
0x88b3e0b282f83009e 1MVDYgVesrxeMuyEHb7zxW3Nb3KjqbfyJ 029824b2d17445069bf9ea7fdb221871cbf4d5069bacc50865e64b4abe4b2a949e
0x8ad59bb29c80889ef 1MVDYgVfM2BvwiofJmYPqL5T5XM7kHWhM 03bb9075fab9e1c28e25e13dd9ded85f22eb4ae2ee883ce331e2182d6c95fa5001
0x8e86c364e15b88007 1MVDYgVcpNBXMhwDW8xgtDQ657NcRNXWM 02e2e6d3ff30c09a8a237b38faaa44b0952ec316df6078cff7a6ef614d1839c94d
0x9103f91ff55f4952f 1MVDYgVfLc65da7jfMyEtuWjDkpiRkN6a 02c0a826f58c00453aca375fbd8d8fbf14931a471a456dc240252db35bb4a54b2d
0x91ab23bdc6bcb328c 1MVDYgVf9X8h4gBMAW2viqGtCPN4Gfmr5 02ed86c4404ce74e42d51de34bcbec1f2b127b8f5141cb789a591fef4b40ce6c91
0x96217b861e12b9b96 1MVDYgVeHQUG1xnGLLRxxaWRqiVx7dtsR 034e97834e9c111136850c6be561b7699be6c17e4a1d8e8b15b158a1eb2f913927
0x975a8968afa93141c 1MVDYgVejxoD691FhZkqwLNsXr3HY6avJ 0204e53e5acf48770a435f1e2d0256f9a8d601cd5094d114778943ab7c4301f1a1
0x977964d6f60cbf2d9 1MVDYgVeC1LEcpdfyVYLhYGC8TPv4VHLV 0331ef8a350a2f20dbdc5c9865e2c1768570002986b4c019496c155c1fa8495b0b
0x996239a0da4cb78a8 1MVDYgVeNZHw9uKhohqfJcJxsXte1dRPG 0241e8d4f4f6b31c31697bf629b2a6f1e291eb34f64b63f2b336085cb1d329e842
0x9d5e6590c5f78ddba 1MVDYgVdadL4qMR1hLyyBa4j8fSM6XkMT 021ae1c43beffefb7e478ec8a2a836b4fb906c7e9ecaf895cd81209a10b54d405c
0x9ff9be752cdfa2884 1MVDYgVdRVv8Ur1dmTpTYdukoTLiraWEo 03df2947a14fbf5564b6e0d6a62680a4248d1788f129d11627f0256fc38ad6e75f
0xa40f0213ddd965953 1MVDYgVebshZjmkWveyyho1sHdpb7USwT 03ac8a1d88ef0c64748fda3026bffc0dbcdd1ca7ed478897ead019eaa492753963
0xa68ecf0c25de252fa 1MVDYgVe12BZLd6MGMRRj7NvwEBsqWebs 02691283c48cc6596c765e72f2c20644f2e3029af89bd0ee6061c84f8015980ec7
0xa8d65f7191dd7cdf9 1MVDYgVeTXvxqG11yTSqyFgtv1rj1D8mT 027e903254caf038a9f8721f13fe42233f267e18133394c324fca95f000646308d
0xab4cac702f0c684e2 1MVDYgVdNQJDutD82NjnD9FJPAFE8guF6 020f223ccfe720f8eb62a869924dc7d3b0ee5d63e80bf86cbb24794aeacd7e688d
0xb0031e137c7b1ff41 1MVDYgVdGYyCpCJFifrbgpqTMEYuteqaH 02d45d1fca587f39c086e32b3e657ae58df5b06437a2cda79c66509106d86f72bb
0xb58061c36fa6cf575 1MVDYgVdjy5S3fTjUHX1s22p22vzpNStm 02a380f59310e64a976452e01c46ac290386a75c7a6a991918ac6874edf2f291e3
0xb8444d33a3e90c4b1 1MVDYgVeEgyAMGB1YuGFQR2qx8Qa6pvgt 03678802cba2fd295ea4462ed3fa223f1bae68b0ba5ce1d5ab7bfa59bd0a090ccc
0xbeb19a4daa70f9a54 1MVDYgVdw2MEbxZMQ3dhtSvL3D2oV8BaJ 03efee90fc03b215ff99412da65b0bc0c4fc70c78eff517cd550c007a6a59d00c4
0xc511c38570914087c 1MVDYgVfMrPwCs3LknHLB3U2mjxtWqW48 023149928889168c4ed1e2229ace1b21b5597b4ed4e1e853bee5d4222d3e987d97
0xc5e006698355486cb 1MVDYgVeiCmNE29eh6MmpmExEcXY7eud8 026fe5da0b489869b92886b7fcb42377dc3025a383b54cd89a818ce04529b63874
0xc7bc8515d67c43583 1MVDYgVf9NzyUA73pURaJdYqwmH21kj8Q 03e1d9fc8f75c782fe218cfb6f356113ebc107ea3cc66ef76b3b0c608b68c6ab0d
0xcabdfaa08f2b4955b 1MVDYgVd9iurd9VaRScV1QWPWkj1mTnwh 03bc014236158a450adce0e88f83dbd7d8a97decd21fe7eeb8a4c7b46f409bd53b
0xd4614e77972dd6143 1MVDYgVfTSEs49AFFUNq6EzVzu1Wb5QhB 0373e3ccb526a3dad4540044ba3555ef498f646e530f8f0d78223d77ffab9a8198
0xd6019f357ffaacb54 1MVDYgVds11pn5Z43NYBfpkGG6V2jAWpB 021e58051d89e0ed871f0a1aaa505373ecc31d43a42ee168210a02e246dfeefc24
0xd9c90b9aadd3e0d8d 1MVDYgVdbNLwHuDWSBXP2vbEx3XnqvrUe 0229484889768d3d4ef89c623d6adeed29442c94dfaff85322c67a429d1c882123
0xdaf8d0f952dc74197 1MVDYgVeYoCFXKE2yLaheoUqqcwdYTKhB 02ba45511a67af6eb2c5b098535425f5721376d10419524762190fd43a15c43ca1
0xdcc6a7d71026def78 1MVDYgVfYMC38QzPuq3pzsS7Z1LHGx5qE 03d9529aea733d3e2d2ed6dedcacb8e82382a0a9037f8521f0fd1181a9a88d1433
0xdd536a767df5c5b74 1MVDYgVe3iSXRqFjXmrCT1DyHJrSZWUDj 029d276ca9bebe25198cf152917e17fd99d9e7e605dd4953e401567ad18c022a17
0xdea515860ae23ad0a 1MVDYgVcsDpz5UoYStrFuWUZbS2FnR9oU 0336345c8359e65c72da464ccf4577b8a535ff4d221fdd846055b2af7c317ad69c
0xdf1cde6807737aecb 1MVDYgVfKYNgjcN2Bj6beC1NyukssuTAm 03e03c34047e1f87b78431bbd3637130facc0ad7b935a0ecfc7e9dd524bcd57973
0xe0c3cef16d85eb043 1MVDYgVcgMxmYSiDXjjzA7utJ82rCDmHY 02a97ca2c620bec36478be7ba97b1157c0a95da7176217af08f0bafa13c1c3e405

... 220k more


But now I have another problem: I have so many prefixes, that computing the next ranges to scan takes a longer time than actually scanning the ranges. This mad spiral requires some GPUs to do the computation of the offsets for the GPU computation (of the offsets of the next GPU computation, because too many prefixes...), what do you think??

I'm gonna start swapping some sweet ranges soon with ya guys, tune up your telegrams if you've got your match-9 or longer address prefixes nicely lined up!

Off the grid, training pigeons to broadcast signed messages.
Denevron
Newbie
*
Offline Offline

Activity: 121
Merit: 0


View Profile
April 01, 2025, 07:29:38 PM
 #8471

so there will still be 28453041475240576740 combinations, not 47478523248093572   Grin

Yes, if the script uses 128-bit counters and 128-bit arithmetic. After that, the next step is optimizing this version for AVX2.  Grin

It will be interesting to see)

I've added a check for a max of 6 bytes for now, to discard unsuitable options faster)
bibilgin
Newbie
*
Offline Offline

Activity: 280
Merit: 0


View Profile
April 01, 2025, 08:31:17 PM
 #8472

Do you want to exchange address prefixes?1MVDYgVaSN6iKJMdVvjz5EEm7Vb6Hvpygo
target:1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ
1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ
Are you sure it's in the 68 bit range? Do you have the hex?


OK, I have to finally admit it.

.....
But now I have another problem: I have so many prefixes, that computing the next ranges to scan takes a longer time than actually scanning the ranges. This mad spiral requires some GPUs to do the computation of the offsets for the GPU computation (of the offsets of the next GPU computation, because too many prefixes...), what do you think??

I'm gonna start swapping some sweet ranges soon with ya guys, tune up your telegrams if you've got your match-9 or longer address prefixes nicely lined up!


Am I seeing it wrong?

KTimesG -
Is it starting to search for prefixes? Is it doing calculations? Smiley

Guys, I was away for a while. I was out of town for a few weeks.
I came home today and I'm continuing to scan.
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 08:49:50 PM
 #8473

Do you want to exchange address prefixes?1MVDYgVaSN6iKJMdVvjz5EEm7Vb6Hvpygo
target:1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ
1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ
Are you sure it's in the 68 bit range? Do you have the hex?


OK, I have to finally admit it.

.....
But now I have another problem: I have so many prefixes, that computing the next ranges to scan takes a longer time than actually scanning the ranges. This mad spiral requires some GPUs to do the computation of the offsets for the GPU computation (of the offsets of the next GPU computation, because too many prefixes...), what do you think??

I'm gonna start swapping some sweet ranges soon with ya guys, tune up your telegrams if you've got your match-9 or longer address prefixes nicely lined up!


Am I seeing it wrong?

KTimesG -
Is it starting to search for prefixes? Is it doing calculations? Smiley

Guys, I was away for a while. I was out of town for a few weeks.
I came home today and I'm continuing to scan.

Welcome back  Grin
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 09:44:32 PM
 #8474

Build on Commit b97c178 fixed for Cross-platform terminal functions ( NoMachine1 / Mutagen )

Key changes made:

    Added random number generation to the CombinationGenerator class with a thread-specific seed

    Modified the worker function to:
        Generate random combinations within its assigned range using a uniform distribution
        Use a thread-local random number generator with a unique seed
        Process combinations in random order rather than sequentially

    The CombinationGenerator now has:
        A random() method to generate random combinations
        Thread-safe random number generation with proper seeding
        Fisher-Yates shuffle for generating random combinations

    Each worker thread now:
        Gets its own range of combinations to check
        Processes them in random order within that range
        Uses a unique seed for its random number generator

Code:
#include <iostream>
#include <array>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <queue>
#include <mutex>
#include <cstring>
#include <unordered_map>
#include <cmath>
#include <immintrin.h>
#include <omp.h>
#include <csignal>
#include <random>
#include <algorithm>
#include <getopt.h>

#ifdef _WIN32
    #include <windows.h>
#else
    #include <unistd.h>
#endif

// Include the required headers
#include "sha256_avx2.h"
#include "ripemd160_avx2.h"
#include "SECP256K1.h"
#include "Point.h"
#include "Int.h"
#include "IntGroup.h"

using namespace std;

// Cross-platform terminal functions
void initConsole() {
#ifdef _WIN32
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD mode = 0;
    GetConsoleMode(hConsole, &mode);
    SetConsoleMode(hConsole, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#endif
}

void clearTerminal() {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[2J\033[H";
#endif
    std::cout.flush();
}

void moveCursorTo(int x, int y) {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {(SHORT)x, (SHORT)y};
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[" << y << ";" << x << "H";
#endif
    std::cout.flush();
}

// Configuration defaults
int PUZZLE_NUM = 20;
int WORKERS = max(1, (int)thread::hardware_concurrency());
int FLIP_COUNT = -1;
const __uint128_t REPORT_INTERVAL = 10000000;
static constexpr int POINTS_BATCH_SIZE = 512;
static constexpr int HASH_BATCH_SIZE = 8;

// Historical puzzle data
const unordered_map<int, tuple<int, string, string>> PUZZLE_DATA = {
    {20, {8, "b907c3a2a3b27789dfb509b730dd47703c272868", "357535"}},
    {21, {9, "29a78213caa9eea824acf08022ab9dfc83414f56", "863317"}},
    {22, {11, "7ff45303774ef7a52fffd8011981034b258cb86b", "1811764"}},
    {23, {12, "d0a79df189fe1ad5c306cc70497b358415da579e", "3007503"}},
    {24, {9, "0959e80121f36aea13b3bad361c15dac26189e2f", "5598802"}},
    {25, {12, "2f396b29b27324300d0c59b17c3abc1835bd3dbb", "14428676"}},
    {26, {14, "bfebb73562d4541b32a02ba664d140b5a574792f", "33185509"}},
    {27, {13, "0c7aaf6caa7e5424b63d317f0f8f1f9fa40d5560", "54538862"}},
    {28, {16, "1306b9e4ff56513a476841bac7ba48d69516b1da", "111949941"}},
    {29, {18, "5a416cc9148f4a377b672c8ae5d3287adaafadec", "227634408"}},
    {30, {16, "d39c4704664e1deb76c9331e637564c257d68a08", "400708894"}},
    {31, {13, "d805f6f251f7479ebd853b3d0f4b9b2656d92f1d", "1033162084"}},
    {32, {14, "9e42601eeaedc244e15f17375adb0e2cd08efdc9", "2102388551"}},
    {33, {15, "4e15e5189752d1eaf444dfd6bff399feb0443977", "3093472814"}},
    {34, {16, "f6d67d7983bf70450f295c9cb828daab265f1bfa", "7137437912"}},
    {35, {19, "f6d8ce225ffbdecec170f8298c3fc28ae686df25", "14133072157"}},
    {36, {14, "74b1e012be1521e5d8d75e745a26ced845ea3d37", "20112871792"}},
    {37, {23, "28c30fb11ed1da72e7c4f89c0164756e8a021d", "42387769980"}},
    {38, {21, "b190e2d40cfdeee2cee072954a2be89e7ba39364", "100251560595"}},
    {39, {23, "0b304f2a79a027270276533fe1ed4eff30910876", "146971536592"}},
    {40, {20, "95a156cd21b4a69de969eb6716864f4c8b82a82a", "323724968937"}},
    {41, {25, "d1562eb37357f9e6fc41cb2359f4d3eda4032329", "1003651412950"}},
    {42, {24, "8efb85f9c5b5db2d55973a04128dc7510075ae23", "1458252205147"}},
    {43, {19, "f92044c7924e5525c61207972c253c9fc9f086f7", "2895374552463"}},
    {44, {24, "80df54e1f612f2fc5bdc05c9d21a83aa8d20791e", "7409811047825"}},
    {45, {21, "f0225bfc68a6e17e87cd8b5e60ae3be18f120753", "15404761757071"}},
    {46, {24, "9a012260d01c5113df66c8a8438c9f7a1e3d5dac", "19996463086597"}},
    {47, {27, "f828005d41b0f4fed4c8dca3b06011072cfb07d4", "51408670348612"}},
    {48, {21, "8661cb56d9df0a61f01328b55af7e56a3fe7a2b2", "119666659114170"}},
    {49, {30, "0d2f533966c6578e1111978ca698f8add7fffdf3", "191206974700443"}},
    {50, {29, "de081b76f840e462fa2cdf360173dfaf4a976a47", "409118905032525"}},
    {51, {25, "ef6419cffd7fad7027994354eb8efae223c2dbe7", "611140496167764"}},
    {52, {27, "36af659edbe94453f6344e920d143f1778653ae7", "2058769515153876"}},
    {53, {26, "2f4870ef54fa4b048c1365d42594cc7d3d269551", "4216495639600700"}},
    {54, {30, "cb66763cf7fde659869ae7f06884d9a0f879a092", "6763683971478124"}},
    {55, {31, "db53d9bbd1f3a83b094eeca7dd970bd85b492fa2", "9974455244496707"}},
    {56, {31, "48214c5969ae9f43f75070cea1e2cb41d5bdcccd", "30045390491869460"}},
    {57, {33, "328660ef43f66abe2653fa178452a5dfc594c2a1", "44218742292676575"}},
    {58, {28, "8c2a6071f89c90c4dab5ab295d7729d1b54ea60f", "138245758910846492"}},
    {59, {30, "b14ed3146f5b2c9bde1703deae9ef33af8110210", "199976667976342049"}},
    {60, {31, "cdf8e5c7503a9d22642e3ecfc87817672787b9c5", "525070384258266191"}},
    {61, {25, "68133e19b2dfb9034edf9830a200cfdf38c90cbd", "1135041350219496382"}},
    {62, {35, "e26646db84b0602f32b34b5a62ca3cae1f91b779", "1425787542618654982"}},
    {63, {34, "ef58afb697b094423ce90721fbb19a359ef7c50e", "3908372542507822062"}},
    {64, {34, "3ee4133d991f52fdf6a25c9834e0745ac74248a4", "8993229949524469768"}},
    {65, {37, "52e763a7ddc1aa4fa811578c491c1bc7fd570137", "17799667357578236628"}},
    {66, {35, "20d45a6a762535700ce9e0b216e31994335db8a5", "30568377312064202855"}},
    {67, {31, "739437bb3dd6d1983e66629c5f08c70e52769371", "46346217550346335726"}},
    {68, {34, "e0b8a2baee1b77fc703455f39d51477451fc8cfc", "132656943602386256302"}}
};

// Global variables
vector<unsigned char> TARGET_HASH160_RAW(20);
string TARGET_HASH160;
Int BASE_KEY;
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, __uint128_t, int>> results;

// 128-bit counter
atomic<uint64_t> total_checked_high(0);
atomic<uint64_t> total_checked_low(0);
__uint128_t total_combinations = 0;
vector<string> g_threadPrivateKeys;
mutex progress_mutex;

// Performance tracking
atomic<uint64_t> globalComparedCount(0);
atomic<uint64_t> localComparedCount(0);
double globalElapsedTime = 0.0;
double mkeysPerSec = 0.0;
chrono::time_point<chrono::high_resolution_clock> tStart;

// Helper functions
__uint128_t load_128() {
    return (static_cast<__uint128_t>(total_checked_high.load()) << 64) | total_checked_low.load();
}

void increment_128() {
    uint64_t old_low = total_checked_low.fetch_add(1);
    if (old_low == UINT64_MAX) {
        total_checked_high.fetch_add(1);
    }
}

static string formatElapsedTime(double seconds) {
    int hrs = static_cast<int>(seconds) / 3600;
    int mins = (static_cast<int>(seconds) % 3600) / 60;
    int secs = static_cast<int>(seconds) % 60;
    ostringstream oss;
    oss << setw(2) << setfill('0') << hrs << ":"
        << setw(2) << setfill('0') << mins << ":"
        << setw(2) << setfill('0') << secs;
    return oss.str();
}

static string to_string_128(__uint128_t value) {
    if (value == 0) return "0";
    char buffer[50];
    char* p = buffer + sizeof(buffer);
    *--p = '\0';
    while (value != 0) {
        *--p = "0123456789"[value % 10];
        value /= 10;
    }
    return p;
}

void signalHandler(int signum) {
    stop_event.store(true);
    cout << "\nInterrupt received, shutting down...\n";
}

class CombinationGenerator {
    int n, k;
    vector<int> current;
    mt19937_64 rng;
   
public:
    CombinationGenerator(int n, int k, uint64_t seed = 0) : n(n), k(k), current(k), rng(seed) {
        if (k > n) k = n;
        for (int i = 0; i < k; ++i) current[i] = i;
    }

    static __uint128_t combinations_count(int n, int k) {
        if (k > n) return 0;
        if (k * 2 > n) k = n - k;
        if (k == 0) return 1;

        __uint128_t result = n;
        for(int i = 2; i <= k; ++i) {
            result *= (n - i + 1);
            result /= i;
        }
        return result;
    }

    const vector<int>& get() const { return current; }
 
    bool next() {
        int i = k - 1;
        while (i >= 0 && current[i] == n - k + i) --i;
        if (i < 0) return false;
        ++current[i];
        for (int j = i + 1; j < k; ++j)
            current[j] = current[j-1] + 1;
        return true;
    }
 
    void unrank(__uint128_t rank) {
        __uint128_t total = combinations_count(n, k);
        if (rank >= total) {
            current.clear();
            return;
        }
       
        current.resize(k);
        __uint128_t remaining_rank = rank;
        int a = n;
        int b = k;
        __uint128_t x = (total - 1) - rank;
       
        for (int i = 0; i < k; i++) {
            a = largest_a_where_comb_a_b_le_x(a, b, x);
            current[i] = (n - 1) - a;
            x -= combinations_count(a, b);
            b--;
        }
    }

private:
    int largest_a_where_comb_a_b_le_x(int a, int b, __uint128_t x) const {
        while (a >= b && combinations_count(a, b) > x) a--;
        return a;
    }
};

static void computeHash160BatchBinSingle(int numKeys,
                                       uint8_t pubKeys[][33],
                                       uint8_t hashResults[][20])
{
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> shaInputs;
    alignas(32) array<array<uint8_t, 32>, HASH_BATCH_SIZE> shaOutputs;
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> ripemdInputs;
    alignas(32) array<array<uint8_t, 20>, HASH_BATCH_SIZE> ripemdOutputs;

    const __uint128_t totalBatches = (numKeys + (HASH_BATCH_SIZE - 1)) / HASH_BATCH_SIZE;

    for (__uint128_t batch = 0; batch < totalBatches; batch++) {
        const __uint128_t batchCount = min<__uint128_t>(HASH_BATCH_SIZE, numKeys - batch * HASH_BATCH_SIZE);

        // Prepare SHA-256 input blocks
        for (__uint128_t i = 0; i < batchCount; i++) {
            memset(shaInputs[i].data(), 0, 64);
            memcpy(shaInputs[i].data(), pubKeys[batch * HASH_BATCH_SIZE + i], 33);
            shaInputs[i][33] = 0x80;
            *reinterpret_cast<uint32_t*>(&shaInputs[i][60]) = _byteswap_ulong(33 * 8);
        }
       
        if (batchCount < HASH_BATCH_SIZE) {
            static array<uint8_t, 64> shaPadding = {};
            memset(shaPadding.data(), 0, 64);
            memcpy(shaPadding.data(), pubKeys[0], 33);
            shaPadding[33] = 0x80;
            *reinterpret_cast<uint32_t*>(&shaPadding[60]) = _byteswap_ulong(33 * 8);
            for (__uint128_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                memcpy(shaInputs[i].data(), shaPadding.data(), 64);
            }
        }

        uint8_t* inPtr[HASH_BATCH_SIZE];
        uint8_t* outPtr[HASH_BATCH_SIZE];
        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = shaInputs[i].data();
            outPtr[i] = shaOutputs[i].data();
        }

        sha256avx2_8B(inPtr[0], inPtr[1], inPtr[2], inPtr[3],
                      inPtr[4], inPtr[5], inPtr[6], inPtr[7],
                      outPtr[0], outPtr[1], outPtr[2], outPtr[3],
                      outPtr[4], outPtr[5], outPtr[6], outPtr[7]);

        for (__uint128_t i = 0; i < batchCount; i++) {
            memset(ripemdInputs[i].data(), 0, 64);
            memcpy(ripemdInputs[i].data(), shaOutputs[i].data(), 32);
            ripemdInputs[i][32] = 0x80;
            *reinterpret_cast<uint32_t*>(&ripemdInputs[i][60]) = _byteswap_ulong(256);
        }

        if (batchCount < HASH_BATCH_SIZE) {
            static array<uint8_t, 64> ripemdPadding = {};
            memset(ripemdPadding.data(), 0, 64);
            memcpy(ripemdPadding.data(), shaOutputs[0].data(), 32);
            ripemdPadding[32] = 0x80;
            *reinterpret_cast<uint32_t*>(&ripemdPadding[60]) = _byteswap_ulong(256);
            for (__uint128_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                memcpy(ripemdInputs[i].data(), ripemdPadding.data(), 64);
            }
        }

        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = ripemdInputs[i].data();
            outPtr[i] = ripemdOutputs[i].data();
        }

        ripemd160avx2::ripemd160avx2_32(
            inPtr[0], inPtr[1], inPtr[2], inPtr[3],
            inPtr[4], inPtr[5], inPtr[6], inPtr[7],
            outPtr[0], outPtr[1], outPtr[2], outPtr[3],
            outPtr[4], outPtr[5], outPtr[6], outPtr[7]
        );

        for (__uint128_t i = 0; i < batchCount; i++) {
            memcpy(hashResults[batch * HASH_BATCH_SIZE + i], ripemdOutputs[i].data(), 20);
        }
    }
}

void worker(Secp256K1* secp, int bit_length, int flip_count, int threadId, __uint128_t start, __uint128_t end) {
    const int fullBatchSize = 2 * POINTS_BATCH_SIZE;
    uint8_t localPubKeys[HASH_BATCH_SIZE][33];
    uint8_t localHashResults[HASH_BATCH_SIZE][20];
    int pointIndices[HASH_BATCH_SIZE];
   
    // Precompute points
    vector<Point> plusPoints(POINTS_BATCH_SIZE);
    vector<Point> minusPoints(POINTS_BATCH_SIZE);
    for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
        Int tmp; tmp.SetInt32(i);
        plusPoints[i] = secp->ComputePublicKey(&tmp);
        minusPoints[i] = plusPoints[i];
        minusPoints[i].y.ModNeg();
    }

    // Structure of Arrays
    vector<Int> deltaX(POINTS_BATCH_SIZE);
    IntGroup modGroup(POINTS_BATCH_SIZE);
    vector<Int> pointBatchX(fullBatchSize);
    vector<Int> pointBatchY(fullBatchSize);

    // Random number generation
    random_device rd;
    mt19937_64 rng(rd() + threadId);
    uniform_int_distribution<uint64_t> dist(0, UINT64_MAX);

    CombinationGenerator gen(bit_length, flip_count, rd() + threadId);
    gen.unrank(start);

    for (__uint128_t count = start; !stop_event.load() && count < end; ) {
        Int currentKey;
        currentKey.Set(&BASE_KEY);
       
        const vector<int>& flips = gen.get();
       
        // Apply flips
        for (int pos : flips) {
            Int mask; mask.SetInt32(1); mask.ShiftL(pos);
            Int temp; temp.Set(&currentKey); temp.ShiftR(pos);
            temp.IsEven() ? currentKey.Add(&mask) : currentKey.Sub(&mask);
        }

        // Store private key
        string keyStr = currentKey.GetBase16();
        keyStr = string(64 - keyStr.length(), '0') + keyStr;
        #pragma omp critical
        g_threadPrivateKeys[threadId] = keyStr;

        // Compute public key
        Point startPoint = secp->ComputePublicKey(&currentKey);
        Int startPointX = startPoint.x, startPointY = startPoint.y, startPointXNeg = startPointX;
        startPointXNeg.ModNeg();

        // Compute deltaX values
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            deltaX[i].ModSub(&plusPoints[i].x, &startPointX);
            deltaX[i+1].ModSub(&plusPoints[i+1].x, &startPointX);
            deltaX[i+2].ModSub(&plusPoints[i+2].x, &startPointX);
            deltaX[i+3].ModSub(&plusPoints[i+3].x, &startPointX);
        }
        modGroup.Set(deltaX.data());
        modGroup.ModInv();

        // Process points
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            for (int j = 0; j < 4; j++) {
                // Plus points
                Int deltaY; deltaY.ModSub(&plusPoints[i+j].y, &startPointY);
                Int slope; slope.ModMulK1(&deltaY, &deltaX[i+j]);
                Int slopeSq; slopeSq.ModSquareK1(&slope);
               
                pointBatchX[i+j].Set(&startPointXNeg);
                pointBatchX[i+j].ModAdd(&slopeSq);
                pointBatchX[i+j].ModSub(&plusPoints[i+j].x);
               
                Int diffX; diffX.ModSub(&startPointX, &pointBatchX[i+j]);
                diffX.ModMulK1(&slope);
               
                pointBatchY[i+j].Set(&startPointY);
                pointBatchY[i+j].ModNeg();
                pointBatchY[i+j].ModAdd(&diffX);

                // Minus points
                deltaY.ModSub(&minusPoints[i+j].y, &startPointY);
                slope.ModMulK1(&deltaY, &deltaX[i+j]);
                slopeSq.ModSquareK1(&slope);
               
                pointBatchX[POINTS_BATCH_SIZE+i+j].Set(&startPointXNeg);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModAdd(&slopeSq);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModSub(&minusPoints[i+j].x);
               
                diffX.ModSub(&startPointX, &pointBatchX[POINTS_BATCH_SIZE+i+j]);
                diffX.ModMulK1(&slope);
               
                pointBatchY[POINTS_BATCH_SIZE+i+j].Set(&startPointY);
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModNeg();
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModAdd(&diffX);
            }
        }

        // Process keys in batches
        int localBatchCount = 0;
        for (int i = 0; i < fullBatchSize && localBatchCount < HASH_BATCH_SIZE; i++) {
            Point tempPoint;
            tempPoint.x.Set(&pointBatchX[i]);
            tempPoint.y.Set(&pointBatchY[i]);
           
            // Convert to compressed public key
            localPubKeys[localBatchCount][0] = tempPoint.y.IsEven() ? 0x02 : 0x03;
            for (int j = 0; j < 32; j++) {
                localPubKeys[localBatchCount][1 + j] = pointBatchX[i].GetByte(31 - j);
            }
            pointIndices[localBatchCount] = i;
            localBatchCount++;

            if (localBatchCount == HASH_BATCH_SIZE) {
                computeHash160BatchBinSingle(localBatchCount, localPubKeys, localHashResults);
                localComparedCount += HASH_BATCH_SIZE;
               
                __m256i target = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(TARGET_HASH160_RAW.data()));

                for (int j = 0; j < HASH_BATCH_SIZE; j++) {
                    __m256i result = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(localHashResults[j]));
                   
                    int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(result, target));
                    const int HASH160_MASK = (1 << 20) - 1;
                   
                    if ((mask & HASH160_MASK) == HASH160_MASK) {
                        bool fullMatch = true;
                        for (int k = 0; k < 20; k++) {
                            if (localHashResults[j][k] != TARGET_HASH160_RAW[k]) {
                                fullMatch = false;
                                break;
                            }
                        }
                       
                        if (fullMatch) {
                            auto tEndTime = chrono::high_resolution_clock::now();
                            globalElapsedTime = chrono::duration<double>(tEndTime - tStart).count();
                            mkeysPerSec = (double)(globalComparedCount + localComparedCount) / globalElapsedTime / 1e6;
                           
                            Int foundKey; foundKey.Set(&currentKey);
                            int idx = pointIndices[j];
                            if (idx < POINTS_BATCH_SIZE) {
                                Int offset; offset.SetInt32(idx);
                                foundKey.Add(&offset);
                            } else {
                                Int offset; offset.SetInt32(idx - POINTS_BATCH_SIZE);
                                foundKey.Sub(&offset);
                            }
                           
                            string hexKey = foundKey.GetBase16();
                            hexKey = string(64 - hexKey.length(), '0') + hexKey;
                           
                            lock_guard<mutex> lock(result_mutex);
                            results.push(make_tuple(hexKey, load_128(), flip_count));
                            stop_event.store(true);
                            return;
                        }
                    }
                }
               
                increment_128();
                localBatchCount = 0;

                // Progress reporting
                __uint128_t current_total = load_128();
                if (current_total % REPORT_INTERVAL == 0 || count == end - 1) {
                    auto now = chrono::high_resolution_clock::now();
                    globalElapsedTime = chrono::duration<double>(now - tStart).count();
                   
                    globalComparedCount += localComparedCount;
                    localComparedCount = 0;
                    mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
                   
                    double progress = min(100.0, (double)current_total / total_combinations * 100.0);
                   
                    lock_guard<mutex> lock(progress_mutex);
                    moveCursorTo(0, 9);
                    cout << "Progress: " << fixed << setprecision(6) << progress << "%\n";
                    cout << "Processed: " << to_string_128(current_total) << "\n";
                    cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
                    cout << "Elapsed Time: " << formatElapsedTime(globalElapsedTime) << "\n";
                    cout.flush();

                    if (current_total >= total_combinations) {
                        stop_event.store(true);
                        break;
                    }
                }
            }
        }

        if (!gen.next()) break;
        count++;
    }

    if (!stop_event.load() && load_128() >= total_combinations) {
        stop_event.store(true);
    }
}

void printUsage(const char* programName) {
    cout << "Usage: " << programName << " [options]\n";
    cout << "Options:\n";
    cout << "  -p, --puzzle NUM    Puzzle number to solve (default: 20)\n";
    cout << "  -t, --threads NUM   Number of CPU cores to use (default: all)\n";
    cout << "  -f, --flips NUM     Override default flip count for puzzle\n";
    cout << "  -h, --help          Show this help message\n";
    cout << "\nExample:\n";
    cout << "  " << programName << " -p 38 -t 8 -f 21\n";
}

int main(int argc, char* argv[]) {
    signal(SIGINT, signalHandler);
   
    // Parse command line arguments
    int opt;
    int option_index = 0;
    static struct option long_options[] = {
        {"puzzle", required_argument, 0, 'p'},
        {"threads", required_argument, 0, 't'},
        {"flips", required_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    while ((opt = getopt_long(argc, argv, "p:t:f:h", long_options, &option_index)) != -1) {
        switch (opt) {
            case 'p':
                PUZZLE_NUM = atoi(optarg);
                if (PUZZLE_NUM < 20 || PUZZLE_NUM > 68) {
                    cerr << "Error: Puzzle number must be between 20 and 68\n";
                    return 1;
                }
                break;
            case 't':
                WORKERS = atoi(optarg);
                if (WORKERS < 1) {
                    cerr << "Error: Thread count must be at least 1\n";
                    return 1;
                }
                break;
            case 'f':
                FLIP_COUNT = atoi(optarg);
                if (FLIP_COUNT < 1) {
                    cerr << "Error: Flip count must be at least 1\n";
                    return 1;
                }
                break;
            case 'h':
                printUsage(argv[0]);
                return 0;
            default:
                printUsage(argv[0]);
                return 1;
        }
    }

    tStart = chrono::high_resolution_clock::now();

    Secp256K1 secp;
    secp.Init();
   
    auto puzzle_it = PUZZLE_DATA.find(PUZZLE_NUM);
    if (puzzle_it == PUZZLE_DATA.end()) {
        cerr << "Error: Invalid puzzle number\n";
        return 1;
    }
   
    auto [DEFAULT_FLIP_COUNT, TARGET_HASH160_HEX, PRIVATE_KEY_DECIMAL] = puzzle_it->second;
   
    if (FLIP_COUNT == -1) FLIP_COUNT = DEFAULT_FLIP_COUNT;
    TARGET_HASH160 = TARGET_HASH160_HEX;
   
    // Convert target hash to bytes
    for (__uint128_t i = 0; i < 20; i++) {
        TARGET_HASH160_RAW[i] = stoul(TARGET_HASH160.substr(i * 2, 2), nullptr, 16);
    }
   
    // Set base key
    BASE_KEY.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
   
    // Verify base key
    Int testKey;
    testKey.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
    if (!testKey.IsEqual(&BASE_KEY)) {
        cerr << "Base key initialization failed!\n";
        return 1;
    }

    if (BASE_KEY.GetBitLength() > PUZZLE_NUM) {
        cerr << "Base key exceeds puzzle bit length!\n";
        return 1;
    }
   
    // Calculate total combinations
    total_combinations = CombinationGenerator::combinations_count(PUZZLE_NUM, FLIP_COUNT);
   
    // Format base key for display
    string paddedKey = BASE_KEY.GetBase16();
    size_t firstNonZero = paddedKey.find_first_not_of('0');
    paddedKey = paddedKey.substr(firstNonZero);
    paddedKey = "0x" + paddedKey;

    // Print initial header
    clearTerminal();
    cout << "=======================================\n";
    cout << "== Mutagen Puzzle Solver by Denevron ==\n";
    cout << "=======================================\n";   
    cout << "Starting puzzle: " << PUZZLE_NUM << " (" << PUZZLE_NUM << "-bit)\n";
    cout << "Target HASH160: " << TARGET_HASH160.substr(0, 10) << "..." << TARGET_HASH160.substr(TARGET_HASH160.length()-10) << "\n";
    cout << "Base Key: " << paddedKey << "\n";
    cout << "Flip count: " << FLIP_COUNT << " ";
    if (FLIP_COUNT != DEFAULT_FLIP_COUNT) {
        cout << "(override, default was " << DEFAULT_FLIP_COUNT << ")";
    }
    cout << "\n";
    cout << "Total combinations for C(" << PUZZLE_NUM << "," << FLIP_COUNT << "): " << to_string_128(total_combinations) << "\n";
    cout << "Using: " << WORKERS << " threads\n";
    // Print empty lines for progress display
    cout << "Progress: 0.000000%\n";
    cout << "Processed: 0\n";
    cout << "Speed: 0.00 Mkeys/s\n";
    cout << "Elapsed Time: 00:00:00\n";

    g_threadPrivateKeys.resize(WORKERS, "0");
    vector<thread> threads;
   
    // Distribute work
    __uint128_t comb_per_thread = total_combinations / WORKERS;
    __uint128_t remainder = total_combinations % WORKERS;
   
    for (int i = 0; i < WORKERS; i++) {
        __uint128_t start = i * comb_per_thread + min((__uint128_t)i, remainder);
        __uint128_t end = start + comb_per_thread + (i < remainder ? 1 : 0);
        threads.emplace_back(worker, &secp, PUZZLE_NUM, FLIP_COUNT, i, start, end);
    }
   
    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }
   
    if (!results.empty()) {
        auto [hex_key, checked, flips] = results.front();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;

        string compactHex = hex_key;
        size_t firstNonZero = compactHex.find_first_not_of('0');
        compactHex = "0x" + compactHex.substr(firstNonZero);

        cout << "=======================================\n";
        cout << "=========== SOLUTION FOUND ============\n";
        cout << "=======================================\n";
        cout << "Private key: " << compactHex << "\n";
        cout << "Checked " << to_string_128(checked) << " combinations\n";
        cout << "Bit flips: " << flips << endl;
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
       
        // Save solution
        ofstream out("puzzle_" + to_string(PUZZLE_NUM) + "_solution.txt");
        if (out) {
            out << hex_key;
            out.close();
            cout << "Solution saved to puzzle_" << PUZZLE_NUM << "_solution.txt\n";
        } else {
            cerr << "Failed to save solution to file!\n";
        }
    } else {
        __uint128_t final_count = load_128();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
       
        cout << "\n\nNo solution found. Checked " << to_string_128(load_128()) << " combinations\n";
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
    }
   
    return 0;
}

No speed loss anymore(?)
Works on puzzle 68.

compiled okay!
nomachine
Full Member
***
Offline Offline

Activity: 812
Merit: 134



View Profile
April 01, 2025, 09:48:04 PM
 #8475

so there will still be 28453041475240576740 combinations, not 47478523248093572   Grin

Yes, if the script uses 128-bit counters and 128-bit arithmetic. After that, the next step is optimizing this version for AVX2.  Grin

It will be interesting to see)

I've added a check for a max of 6 bytes for now, to discard unsuitable options faster)

https://github.com/NoMachine1/Mutagen/

BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 09:52:44 PM
 #8476

so there will still be 28453041475240576740 combinations, not 47478523248093572   Grin

Yes, if the script uses 128-bit counters and 128-bit arithmetic. After that, the next step is optimizing this version for AVX2.  Grin

It will be interesting to see)

I've added a check for a max of 6 bytes for now, to discard unsuitable options faster)

https://github.com/NoMachine1/Mutagen/

sequential?
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 09:55:12 PM
 #8477


Quote

Error compiling!

Code:
#include <iostream>
#include <array>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <queue>
#include <mutex>
#include <cstring>
#include <unordered_map>
#include <cmath>
#include <immintrin.h>
#include <omp.h>
#include <csignal>
#include <random>
#include <algorithm>
#include <getopt.h>

#ifdef _WIN32
    #include <windows.h>
#else
    #include <unistd.h>
#endif

// Include the required headers
#include "sha256_avx2.h"
#include "ripemd160_avx2.h"
#include "SECP256K1.h"
#include "Point.h"
#include "Int.h"
#include "IntGroup.h"

using namespace std;

// Cross-platform terminal functions
void initConsole() {
#ifdef _WIN32
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD mode = 0;
    GetConsoleMode(hConsole, &mode);
    SetConsoleMode(hConsole, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#endif
}

void clearTerminal() {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[2J\033[H";
#endif
    std::cout.flush();
}

void moveCursorTo(int x, int y) {
#ifdef _WIN32
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {(SHORT)x, (SHORT)y};
    SetConsoleCursorPosition(hStdOut, coord);
#else
    std::cout << "\033[" << y << ";" << x << "H";
#endif
    std::cout.flush();
}

// Configuration defaults
int PUZZLE_NUM = 20;
int WORKERS = max(1, (int)thread::hardware_concurrency());
int FLIP_COUNT = -1;
const __uint128_t REPORT_INTERVAL = 10000000;
static constexpr int POINTS_BATCH_SIZE = 512;
static constexpr int HASH_BATCH_SIZE = 8;

// Historical puzzle data
const unordered_map<int, tuple<int, string, string>> PUZZLE_DATA = {
    {20, {8, "b907c3a2a3b27789dfb509b730dd47703c272868", "357535"}},
    {21, {9, "29a78213caa9eea824acf08022ab9dfc83414f56", "863317"}},
    {22, {11, "7ff45303774ef7a52fffd8011981034b258cb86b", "1811764"}},
    {23, {12, "d0a79df189fe1ad5c306cc70497b358415da579e", "3007503"}},
    {24, {9, "0959e80121f36aea13b3bad361c15dac26189e2f", "5598802"}},
    {25, {12, "2f396b29b27324300d0c59b17c3abc1835bd3dbb", "14428676"}},
    {26, {14, "bfebb73562d4541b32a02ba664d140b5a574792f", "33185509"}},
    {27, {13, "0c7aaf6caa7e5424b63d317f0f8f1f9fa40d5560", "54538862"}},
    {28, {16, "1306b9e4ff56513a476841bac7ba48d69516b1da", "111949941"}},
    {29, {18, "5a416cc9148f4a377b672c8ae5d3287adaafadec", "227634408"}},
    {30, {16, "d39c4704664e1deb76c9331e637564c257d68a08", "400708894"}},
    {31, {13, "d805f6f251f7479ebd853b3d0f4b9b2656d92f1d", "1033162084"}},
    {32, {14, "9e42601eeaedc244e15f17375adb0e2cd08efdc9", "2102388551"}},
    {33, {15, "4e15e5189752d1eaf444dfd6bff399feb0443977", "3093472814"}},
    {34, {16, "f6d67d7983bf70450f295c9cb828daab265f1bfa", "7137437912"}},
    {35, {19, "f6d8ce225ffbdecec170f8298c3fc28ae686df25", "14133072157"}},
    {36, {14, "74b1e012be1521e5d8d75e745a26ced845ea3d37", "20112871792"}},
    {37, {23, "28c30fb11ed1da72e7c4f89c0164756e8a021d", "42387769980"}},
    {38, {21, "b190e2d40cfdeee2cee072954a2be89e7ba39364", "100251560595"}},
    {39, {23, "0b304f2a79a027270276533fe1ed4eff30910876", "146971536592"}},
    {40, {20, "95a156cd21b4a69de969eb6716864f4c8b82a82a", "323724968937"}},
    {41, {25, "d1562eb37357f9e6fc41cb2359f4d3eda4032329", "1003651412950"}},
    {42, {24, "8efb85f9c5b5db2d55973a04128dc7510075ae23", "1458252205147"}},
    {43, {19, "f92044c7924e5525c61207972c253c9fc9f086f7", "2895374552463"}},
    {44, {24, "80df54e1f612f2fc5bdc05c9d21a83aa8d20791e", "7409811047825"}},
    {45, {21, "f0225bfc68a6e17e87cd8b5e60ae3be18f120753", "15404761757071"}},
    {46, {24, "9a012260d01c5113df66c8a8438c9f7a1e3d5dac", "19996463086597"}},
    {47, {27, "f828005d41b0f4fed4c8dca3b06011072cfb07d4", "51408670348612"}},
    {48, {21, "8661cb56d9df0a61f01328b55af7e56a3fe7a2b2", "119666659114170"}},
    {49, {30, "0d2f533966c6578e1111978ca698f8add7fffdf3", "191206974700443"}},
    {50, {29, "de081b76f840e462fa2cdf360173dfaf4a976a47", "409118905032525"}},
    {51, {25, "ef6419cffd7fad7027994354eb8efae223c2dbe7", "611140496167764"}},
    {52, {27, "36af659edbe94453f6344e920d143f1778653ae7", "2058769515153876"}},
    {53, {26, "2f4870ef54fa4b048c1365d42594cc7d3d269551", "4216495639600700"}},
    {54, {30, "cb66763cf7fde659869ae7f06884d9a0f879a092", "6763683971478124"}},
    {55, {31, "db53d9bbd1f3a83b094eeca7dd970bd85b492fa2", "9974455244496707"}},
    {56, {31, "48214c5969ae9f43f75070cea1e2cb41d5bdcccd", "30045390491869460"}},
    {57, {33, "328660ef43f66abe2653fa178452a5dfc594c2a1", "44218742292676575"}},
    {58, {28, "8c2a6071f89c90c4dab5ab295d7729d1b54ea60f", "138245758910846492"}},
    {59, {30, "b14ed3146f5b2c9bde1703deae9ef33af8110210", "199976667976342049"}},
    {60, {31, "cdf8e5c7503a9d22642e3ecfc87817672787b9c5", "525070384258266191"}},
    {61, {25, "68133e19b2dfb9034edf9830a200cfdf38c90cbd", "1135041350219496382"}},
    {62, {35, "e26646db84b0602f32b34b5a62ca3cae1f91b779", "1425787542618654982"}},
    {63, {34, "ef58afb697b094423ce90721fbb19a359ef7c50e", "3908372542507822062"}},
    {64, {34, "3ee4133d991f52fdf6a25c9834e0745ac74248a4", "8993229949524469768"}},
    {65, {37, "52e763a7ddc1aa4fa811578c491c1bc7fd570137", "17799667357578236628"}},
    {66, {35, "20d45a6a762535700ce9e0b216e31994335db8a5", "30568377312064202855"}},
    {67, {31, "739437bb3dd6d1983e66629c5f08c70e52769371", "46346217550346335726"}},
    {68, {34, "e0b8a2baee1b77fc703455f39d51477451fc8cfc", "132656943602386256302"}}
};

// Global variables
vector<unsigned char> TARGET_HASH160_RAW(20);
string TARGET_HASH160;
Int BASE_KEY;
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, __uint128_t, int>> results;

// 128-bit counter
atomic<uint64_t> total_checked_high(0);
atomic<uint64_t> total_checked_low(0);
__uint128_t total_combinations = 0;
vector<string> g_threadPrivateKeys;
mutex progress_mutex;

// Performance tracking
atomic<uint64_t> globalComparedCount(0);
atomic<uint64_t> localComparedCount(0);
double globalElapsedTime = 0.0;
double mkeysPerSec = 0.0;
chrono::time_point<chrono::high_resolution_clock> tStart;

// Helper functions
__uint128_t load_128() {
    return (static_cast<__uint128_t>(total_checked_high.load()) << 64) | total_checked_low.load();
}

void increment_128() {
    uint64_t old_low = total_checked_low.fetch_add(1);
    if (old_low == UINT64_MAX) {
        total_checked_high.fetch_add(1);
    }
}

static string formatElapsedTime(double seconds) {
    int hrs = static_cast<int>(seconds) / 3600;
    int mins = (static_cast<int>(seconds) % 3600) / 60;
    int secs = static_cast<int>(seconds) % 60;
    ostringstream oss;
    oss << setw(2) << setfill('0') << hrs << ":"
        << setw(2) << setfill('0') << mins << ":"
        << setw(2) << setfill('0') << secs;
    return oss.str();
}

static string to_string_128(__uint128_t value) {
    if (value == 0) return "0";
    char buffer[50];
    char* p = buffer + sizeof(buffer);
    *--p = '\0';
    while (value != 0) {
        *--p = "0123456789"[value % 10];
        value /= 10;
    }
    return p;
}

void signalHandler(int signum) {
    stop_event.store(true);
    cout << "\nInterrupt received, shutting down...\n";
}

class CombinationGenerator {
    int n, k;
    vector<int> current;
    mt19937_64 rng;
   
public:
    CombinationGenerator(int n, int k, uint64_t seed = 0) : n(n), k(k), current(k), rng(seed) {
        if (k > n) k = n;
        for (int i = 0; i < k; ++i) current[i] = i;
    }

    static __uint128_t combinations_count(int n, int k) {
        if (k > n) return 0;
        if (k * 2 > n) k = n - k;
        if (k == 0) return 1;

        __uint128_t result = n;
        for(int i = 2; i <= k; ++i) {
            result *= (n - i + 1);
            result /= i;
        }
        return result;
    }

    const vector<int>& get() const { return current; }
 
    bool next() {
        int i = k - 1;
        while (i >= 0 && current[i] == n - k + i) --i;
        if (i < 0) return false;
        ++current[i];
        for (int j = i + 1; j < k; ++j)
            current[j] = current[j-1] + 1;
        return true;
    }
 
    void unrank(__uint128_t rank) {
        __uint128_t total = combinations_count(n, k);
        if (rank >= total) {
            current.clear();
            return;
        }
       
        current.resize(k);
        __uint128_t remaining_rank = rank;
        int a = n;
        int b = k;
        __uint128_t x = (total - 1) - rank;
       
        for (int i = 0; i < k; i++) {
            a = largest_a_where_comb_a_b_le_x(a, b, x);
            current[i] = (n - 1) - a;
            x -= combinations_count(a, b);
            b--;
        }
    }

private:
    int largest_a_where_comb_a_b_le_x(int a, int b, __uint128_t x) const {
        while (a >= b && combinations_count(a, b) > x) a--;
        return a;
    }
};

static void computeHash160BatchBinSingle(int numKeys,
                                       uint8_t pubKeys[][33],
                                       uint8_t hashResults[][20])
{
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> shaInputs;
    alignas(32) array<array<uint8_t, 32>, HASH_BATCH_SIZE> shaOutputs;
    alignas(32) array<array<uint8_t, 64>, HASH_BATCH_SIZE> ripemdInputs;
    alignas(32) array<array<uint8_t, 20>, HASH_BATCH_SIZE> ripemdOutputs;

    const __uint128_t totalBatches = (numKeys + (HASH_BATCH_SIZE - 1)) / HASH_BATCH_SIZE;

    for (__uint128_t batch = 0; batch < totalBatches; batch++) {
        const __uint128_t batchCount = min<__uint128_t>(HASH_BATCH_SIZE, numKeys - batch * HASH_BATCH_SIZE);

        // Prepare SHA-256 input blocks
        for (__uint128_t i = 0; i < batchCount; i++) {
            memset(shaInputs[i].data(), 0, 64);
            memcpy(shaInputs[i].data(), pubKeys[batch * HASH_BATCH_SIZE + i], 33);
            shaInputs[i][33] = 0x80;
            *reinterpret_cast<uint32_t*>(&shaInputs[i][60]) = _byteswap_ulong(33 * 8);
        }
       
        if (batchCount < HASH_BATCH_SIZE) {
            static array<uint8_t, 64> shaPadding = {};
            memset(shaPadding.data(), 0, 64);
            memcpy(shaPadding.data(), pubKeys[0], 33);
            shaPadding[33] = 0x80;
            *reinterpret_cast<uint32_t*>(&shaPadding[60]) = _byteswap_ulong(33 * 8);
            for (__uint128_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                memcpy(shaInputs[i].data(), shaPadding.data(), 64);
            }
        }

        uint8_t* inPtr[HASH_BATCH_SIZE];
        uint8_t* outPtr[HASH_BATCH_SIZE];
        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = shaInputs[i].data();
            outPtr[i] = shaOutputs[i].data();
        }

        sha256avx2_8B(inPtr[0], inPtr[1], inPtr[2], inPtr[3],
                      inPtr[4], inPtr[5], inPtr[6], inPtr[7],
                      outPtr[0], outPtr[1], outPtr[2], outPtr[3],
                      outPtr[4], outPtr[5], outPtr[6], outPtr[7]);

        for (__uint128_t i = 0; i < batchCount; i++) {
            memset(ripemdInputs[i].data(), 0, 64);
            memcpy(ripemdInputs[i].data(), shaOutputs[i].data(), 32);
            ripemdInputs[i][32] = 0x80;
            *reinterpret_cast<uint32_t*>(&ripemdInputs[i][60]) = _byteswap_ulong(256);
        }

        if (batchCount < HASH_BATCH_SIZE) {
            static array<uint8_t, 64> ripemdPadding = {};
            memset(ripemdPadding.data(), 0, 64);
            memcpy(ripemdPadding.data(), shaOutputs[0].data(), 32);
            ripemdPadding[32] = 0x80;
            *reinterpret_cast<uint32_t*>(&ripemdPadding[60]) = _byteswap_ulong(256);
            for (__uint128_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
                memcpy(ripemdInputs[i].data(), ripemdPadding.data(), 64);
            }
        }

        for (int i = 0; i < HASH_BATCH_SIZE; i++) {
            inPtr[i]  = ripemdInputs[i].data();
            outPtr[i] = ripemdOutputs[i].data();
        }

        ripemd160avx2::ripemd160avx2_32(
            inPtr[0], inPtr[1], inPtr[2], inPtr[3],
            inPtr[4], inPtr[5], inPtr[6], inPtr[7],
            outPtr[0], outPtr[1], outPtr[2], outPtr[3],
            outPtr[4], outPtr[5], outPtr[6], outPtr[7]
        );

        for (__uint128_t i = 0; i < batchCount; i++) {
            memcpy(hashResults[batch * HASH_BATCH_SIZE + i], ripemdOutputs[i].data(), 20);
        }
    }
}

void worker(Secp256K1* secp, int bit_length, int flip_count, int threadId, __uint128_t start, __uint128_t end) {
    const int fullBatchSize = 2 * POINTS_BATCH_SIZE;
    uint8_t localPubKeys[HASH_BATCH_SIZE][33];
    uint8_t localHashResults[HASH_BATCH_SIZE][20];
    int pointIndices[HASH_BATCH_SIZE];
   
    // Precompute points
    vector<Point> plusPoints(POINTS_BATCH_SIZE);
    vector<Point> minusPoints(POINTS_BATCH_SIZE);
    for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
        Int tmp; tmp.SetInt32(i);
        plusPoints[i] = secp->ComputePublicKey(&tmp);
        minusPoints[i] = plusPoints[i];
        minusPoints[i].y.ModNeg();
    }

    // Structure of Arrays
    vector<Int> deltaX(POINTS_BATCH_SIZE);
    IntGroup modGroup(POINTS_BATCH_SIZE);
    vector<Int> pointBatchX(fullBatchSize);
    vector<Int> pointBatchY(fullBatchSize);

    // Random number generation
    random_device rd;
    mt19937_64 rng(rd() + threadId);
    uniform_int_distribution<uint64_t> dist(0, UINT64_MAX);

    CombinationGenerator gen(bit_length, flip_count, rd() + threadId);
    gen.unrank(start);

    for (__uint128_t count = start; !stop_event.load() && count < end; ) {
        Int currentKey;
        currentKey.Set(&BASE_KEY);
       
        const vector<int>& flips = gen.get();
       
        // Apply flips
        for (int pos : flips) {
            Int mask; mask.SetInt32(1); mask.ShiftL(pos);
            Int temp; temp.Set(&currentKey); temp.ShiftR(pos);
            temp.IsEven() ? currentKey.Add(&mask) : currentKey.Sub(&mask);
        }

        // Store private key
        string keyStr = currentKey.GetBase16();
        keyStr = string(64 - keyStr.length(), '0') + keyStr;
        #pragma omp critical
        g_threadPrivateKeys[threadId] = keyStr;

        // Compute public key
        Point startPoint = secp->ComputePublicKey(&currentKey);
        Int startPointX = startPoint.x, startPointY = startPoint.y, startPointXNeg = startPointX;
        startPointXNeg.ModNeg();

        // Compute deltaX values
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            deltaX[i].ModSub(&plusPoints[i].x, &startPointX);
            deltaX[i+1].ModSub(&plusPoints[i+1].x, &startPointX);
            deltaX[i+2].ModSub(&plusPoints[i+2].x, &startPointX);
            deltaX[i+3].ModSub(&plusPoints[i+3].x, &startPointX);
        }
        modGroup.Set(deltaX.data());
        modGroup.ModInv();

        // Process points
        for (int i = 0; i < POINTS_BATCH_SIZE; i += 4) {
            for (int j = 0; j < 4; j++) {
                // Plus points
                Int deltaY; deltaY.ModSub(&plusPoints[i+j].y, &startPointY);
                Int slope; slope.ModMulK1(&deltaY, &deltaX[i+j]);
                Int slopeSq; slopeSq.ModSquareK1(&slope);
               
                pointBatchX[i+j].Set(&startPointXNeg);
                pointBatchX[i+j].ModAdd(&slopeSq);
                pointBatchX[i+j].ModSub(&plusPoints[i+j].x);
               
                Int diffX; diffX.ModSub(&startPointX, &pointBatchX[i+j]);
                diffX.ModMulK1(&slope);
               
                pointBatchY[i+j].Set(&startPointY);
                pointBatchY[i+j].ModNeg();
                pointBatchY[i+j].ModAdd(&diffX);

                // Minus points
                deltaY.ModSub(&minusPoints[i+j].y, &startPointY);
                slope.ModMulK1(&deltaY, &deltaX[i+j]);
                slopeSq.ModSquareK1(&slope);
               
                pointBatchX[POINTS_BATCH_SIZE+i+j].Set(&startPointXNeg);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModAdd(&slopeSq);
                pointBatchX[POINTS_BATCH_SIZE+i+j].ModSub(&minusPoints[i+j].x);
               
                diffX.ModSub(&startPointX, &pointBatchX[POINTS_BATCH_SIZE+i+j]);
                diffX.ModMulK1(&slope);
               
                pointBatchY[POINTS_BATCH_SIZE+i+j].Set(&startPointY);
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModNeg();
                pointBatchY[POINTS_BATCH_SIZE+i+j].ModAdd(&diffX);
            }
        }

        // Process keys in batches
        int localBatchCount = 0;
        for (int i = 0; i < fullBatchSize && localBatchCount < HASH_BATCH_SIZE; i++) {
            Point tempPoint;
            tempPoint.x.Set(&pointBatchX[i]);
            tempPoint.y.Set(&pointBatchY[i]);
           
            // Convert to compressed public key
            localPubKeys[localBatchCount][0] = tempPoint.y.IsEven() ? 0x02 : 0x03;
            for (int j = 0; j < 32; j++) {
                localPubKeys[localBatchCount][1 + j] = pointBatchX[i].GetByte(31 - j);
            }
            pointIndices[localBatchCount] = i;
            localBatchCount++;

            if (localBatchCount == HASH_BATCH_SIZE) {
                computeHash160BatchBinSingle(localBatchCount, localPubKeys, localHashResults);
                localComparedCount += HASH_BATCH_SIZE;
               
                __m256i target = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(TARGET_HASH160_RAW.data()));

                for (int j = 0; j < HASH_BATCH_SIZE; j++) {
                    __m256i result = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(localHashResults[j]));
                   
                    int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(result, target));
                    const int HASH160_MASK = (1 << 20) - 1;
                   
                    if ((mask & HASH160_MASK) == HASH160_MASK) {
                        bool fullMatch = true;
                        for (int k = 0; k < 20; k++) {
                            if (localHashResults[j][k] != TARGET_HASH160_RAW[k]) {
                                fullMatch = false;
                                break;
                            }
                        }
                       
                        if (fullMatch) {
                            auto tEndTime = chrono::high_resolution_clock::now();
                            globalElapsedTime = chrono::duration<double>(tEndTime - tStart).count();
                            mkeysPerSec = (double)(globalComparedCount + localComparedCount) / globalElapsedTime / 1e6;
                           
                            Int foundKey; foundKey.Set(&currentKey);
                            int idx = pointIndices[j];
                            if (idx < POINTS_BATCH_SIZE) {
                                Int offset; offset.SetInt32(idx);
                                foundKey.Add(&offset);
                            } else {
                                Int offset; offset.SetInt32(idx - POINTS_BATCH_SIZE);
                                foundKey.Sub(&offset);
                            }
                           
                            string hexKey = foundKey.GetBase16();
                            hexKey = string(64 - hexKey.length(), '0') + hexKey;
                           
                            lock_guard<mutex> lock(result_mutex);
                            results.push(make_tuple(hexKey, load_128(), flip_count));
                            stop_event.store(true);
                            return;
                        }
                    }
                }
               
                increment_128();
                localBatchCount = 0;

                // Progress reporting
                __uint128_t current_total = load_128();
                if (current_total % REPORT_INTERVAL == 0 || count == end - 1) {
                    auto now = chrono::high_resolution_clock::now();
                    globalElapsedTime = chrono::duration<double>(now - tStart).count();
                   
                    globalComparedCount += localComparedCount;
                    localComparedCount = 0;
                    mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
                   
                    double progress = min(100.0, (double)current_total / total_combinations * 100.0);
                   
                    lock_guard<mutex> lock(progress_mutex);
                    moveCursorTo(0, 9);
                    cout << "Progress: " << fixed << setprecision(6) << progress << "%\n";
                    cout << "Processed: " << to_string_128(current_total) << "\n";
                    cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
                    cout << "Elapsed Time: " << formatElapsedTime(globalElapsedTime) << "\n";
                    cout.flush();

                    if (current_total >= total_combinations) {
                        stop_event.store(true);
                        break;
                    }
                }
            }
        }

        if (!gen.next()) break;
        count++;
    }

    if (!stop_event.load() && load_128() >= total_combinations) {
        stop_event.store(true);
    }
}

void printUsage(const char* programName) {
    cout << "Usage: " << programName << " [options]\n";
    cout << "Options:\n";
    cout << "  -p, --puzzle NUM    Puzzle number to solve (default: 20)\n";
    cout << "  -t, --threads NUM   Number of CPU cores to use (default: all)\n";
    cout << "  -f, --flips NUM     Override default flip count for puzzle\n";
    cout << "  -h, --help          Show this help message\n";
    cout << "\nExample:\n";
    cout << "  " << programName << " -p 38 -t 8 -f 21\n";
}

int main(int argc, char* argv[]) {
    signal(SIGINT, signalHandler);
   
    // Parse command line arguments
    int opt;
    int option_index = 0;
    static struct option long_options[] = {
        {"puzzle", required_argument, 0, 'p'},
        {"threads", required_argument, 0, 't'},
        {"flips", required_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    while ((opt = getopt_long(argc, argv, "p:t:f:h", long_options, &option_index)) != -1) {
        switch (opt) {
            case 'p':
                PUZZLE_NUM = atoi(optarg);
                if (PUZZLE_NUM < 20 || PUZZLE_NUM > 68) {
                    cerr << "Error: Puzzle number must be between 20 and 68\n";
                    return 1;
                }
                break;
            case 't':
                WORKERS = atoi(optarg);
                if (WORKERS < 1) {
                    cerr << "Error: Thread count must be at least 1\n";
                    return 1;
                }
                break;
            case 'f':
                FLIP_COUNT = atoi(optarg);
                if (FLIP_COUNT < 1) {
                    cerr << "Error: Flip count must be at least 1\n";
                    return 1;
                }
                break;
            case 'h':
                printUsage(argv[0]);
                return 0;
            default:
                printUsage(argv[0]);
                return 1;
        }
    }

    tStart = chrono::high_resolution_clock::now();

    Secp256K1 secp;
    secp.Init();
   
    auto puzzle_it = PUZZLE_DATA.find(PUZZLE_NUM);
    if (puzzle_it == PUZZLE_DATA.end()) {
        cerr << "Error: Invalid puzzle number\n";
        return 1;
    }
   
    auto [DEFAULT_FLIP_COUNT, TARGET_HASH160_HEX, PRIVATE_KEY_DECIMAL] = puzzle_it->second;
   
    if (FLIP_COUNT == -1) FLIP_COUNT = DEFAULT_FLIP_COUNT;
    TARGET_HASH160 = TARGET_HASH160_HEX;
   
    // Convert target hash to bytes
    for (__uint128_t i = 0; i < 20; i++) {
        TARGET_HASH160_RAW[i] = stoul(TARGET_HASH160.substr(i * 2, 2), nullptr, 16);
    }
   
    // Set base key
    BASE_KEY.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
   
    // Verify base key
    Int testKey;
    testKey.SetBase10(const_cast<char*>(PRIVATE_KEY_DECIMAL.c_str()));
    if (!testKey.IsEqual(&BASE_KEY)) {
        cerr << "Base key initialization failed!\n";
        return 1;
    }

    if (BASE_KEY.GetBitLength() > PUZZLE_NUM) {
        cerr << "Base key exceeds puzzle bit length!\n";
        return 1;
    }
   
    // Calculate total combinations
    total_combinations = CombinationGenerator::combinations_count(PUZZLE_NUM, FLIP_COUNT);
   
    // Format base key for display
    string paddedKey = BASE_KEY.GetBase16();
    size_t firstNonZero = paddedKey.find_first_not_of('0');
    paddedKey = paddedKey.substr(firstNonZero);
    paddedKey = "0x" + paddedKey;

    // Print initial header
    clearTerminal();
    cout << "=======================================\n";
    cout << "== Mutagen Puzzle Solver by Denevron ==\n";
    cout << "=======================================\n";   
    cout << "Starting puzzle: " << PUZZLE_NUM << " (" << PUZZLE_NUM << "-bit)\n";
    cout << "Target HASH160: " << TARGET_HASH160.substr(0, 10) << "..." << TARGET_HASH160.substr(TARGET_HASH160.length()-10) << "\n";
    cout << "Base Key: " << paddedKey << "\n";
    cout << "Flip count: " << FLIP_COUNT << " ";
    if (FLIP_COUNT != DEFAULT_FLIP_COUNT) {
        cout << "(override, default was " << DEFAULT_FLIP_COUNT << ")";
    }
    cout << "\n";
    cout << "Total combinations for C(" << PUZZLE_NUM << "," << FLIP_COUNT << "): " << to_string_128(total_combinations) << "\n";
    cout << "Using: " << WORKERS << " threads\n";
    // Print empty lines for progress display
    cout << "Progress: 0.000000%\n";
    cout << "Processed: 0\n";
    cout << "Speed: 0.00 Mkeys/s\n";
    cout << "Elapsed Time: 00:00:00\n";

    g_threadPrivateKeys.resize(WORKERS, "0");
    vector<thread> threads;
   
    // Distribute work
    __uint128_t comb_per_thread = total_combinations / WORKERS;
    __uint128_t remainder = total_combinations % WORKERS;
   
    for (int i = 0; i < WORKERS; i++) {
        __uint128_t start = i * comb_per_thread + min((__uint128_t)i, remainder);
        __uint128_t end = start + comb_per_thread + (i < remainder ? 1 : 0);
        threads.emplace_back(worker, &secp, PUZZLE_NUM, FLIP_COUNT, i, start, end);
    }
   
    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }
   
    if (!results.empty()) {
        auto [hex_key, checked, flips] = results.front();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;

        string compactHex = hex_key;
        size_t firstNonZero = compactHex.find_first_not_of('0');
        compactHex = "0x" + compactHex.substr(firstNonZero);

        cout << "=======================================\n";
        cout << "=========== SOLUTION FOUND ============\n";
        cout << "=======================================\n";
        cout << "Private key: " << compactHex << "\n";
        cout << "Checked " << to_string_128(checked) << " combinations\n";
        cout << "Bit flips: " << flips << endl;
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
       
        // Save solution
        ofstream out("puzzle_" + to_string(PUZZLE_NUM) + "_solution.txt");
        if (out) {
            out << hex_key;
            out.close();
            cout << "Solution saved to puzzle_" << PUZZLE_NUM << "_solution.txt\n";
        } else {
            cerr << "Failed to save solution to file!\n";
        }
    } else {
        __uint128_t final_count = load_128();
        globalElapsedTime = chrono::duration<double>(chrono::high_resolution_clock::now() - tStart).count();
        mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
       
        cout << "\n\nNo solution found. Checked " << to_string_128(load_128()) << " combinations\n";
        cout << "Time: " << fixed << setprecision(2) << globalElapsedTime << " seconds ("
             << formatElapsedTime(globalElapsedTime) << ")\n";
        cout << "Speed: " << fixed << setprecision(2) << mkeysPerSec << " Mkeys/s\n";
    }
   
    return 0;
}

test again

Code:
mutagen.cpp: In function 'void computeHash160BatchBinSingle(int, uint8_t (*)[33], uint8_t (*)[20])':
mutagen.cpp:280:63: error: '_byteswap_ulong' was not declared in this scope; did you mean '_byteswap_uint64'?
  280 |             *reinterpret_cast<uint32_t*>(&shaInputs[i][60]) = _byteswap_ulong(33 * 8);
      |                                                               ^~~~~~~~~~~~~~~
      |                                                               _byteswap_uint64
mutagen.cpp:288:61: error: '_byteswap_ulong' was not declared in this scope; did you mean '_byteswap_uint64'?
  288 |             *reinterpret_cast<uint32_t*>(&shaPadding[60]) = _byteswap_ulong(33 * 8);
      |                                                             ^~~~~~~~~~~~~~~
      |                                                             _byteswap_uint64
mutagen.cpp:310:66: error: '_byteswap_ulong' was not declared in this scope; did you mean '_byteswap_uint64'?
  310 |             *reinterpret_cast<uint32_t*>(&ripemdInputs[i][60]) = _byteswap_ulong(256);
      |                                                                  ^~~~~~~~~~~~~~~
      |                                                                  _byteswap_uint64
mutagen.cpp:318:64: error: '_byteswap_ulong' was not declared in this scope; did you mean '_byteswap_uint64'?
  318 |             *reinterpret_cast<uint32_t*>(&ripemdPadding[60]) = _byteswap_ulong(256);
      |                                                                ^~~~~~~~~~~~~~~
      |                                                                _byteswap_uint64
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 10:01:46 PM
Last edit: April 04, 2025, 03:35:35 PM by hilariousandco
 #8478

windows compiles ok..

i dont get it, that part not changed


I will double check, I am using macOS  Roll Eyes


Yes and also the newer version of this one https://github.com/NoMachine1/Mutagen/ as well

PS: Tested in Linux won't compiled either (Fixed on macOS and Linux as well adding a file bytesswap_compat.h) cuz "Your code is using _byteswap_ulong, which is a Windows-specific function"
bibilgin
Newbie
*
Offline Offline

Activity: 280
Merit: 0


View Profile
April 01, 2025, 10:11:45 PM
 #8479


Welcome back  Grin

Thanks man.

I read some of the pages back. I saw some good developments. Thanks to everyone who contributed.

I hope someone who really needs it finds it. Good Luck.
b0dre
Jr. Member
*
Offline Offline

Activity: 61
Merit: 1


View Profile
April 01, 2025, 10:18:05 PM
 #8480


Welcome back  Grin

Thanks man.

I read some of the pages back. I saw some good developments. Thanks to everyone who contributed.

I hope someone who really needs it finds it. Good Luck.

Yes, in CPU the speed blow up, Good Luck you too
Pages: « 1 ... 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 [424] 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 ... 652 »
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!