Shared Code

by phtuyul7·3 views·Aug 17, 2026Open in Playground →
main.cu
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
#include <cuda_runtime.h>
#include <curand_kernel.h>

#include <openssl/evp.h>

#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

static constexpr int SEED_SIZE = 32;
static constexpr int PUB_SIZE  = 32;
static constexpr int KEY_SIZE  = 64;

static std::atomic<bool> found(false);
static std::atomic<uint64_t> tested(0);

static std::mutex result_mutex;

struct Result {
    uint8_t seed[32];
    uint8_t pub[32];
    std::string address;
};

static Result result;

/*
 * Solana uses Bitcoin-style Base58.
 */
static const char BASE58[] =
    "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

std::string base58_encode(const uint8_t* data, size_t len)
{
    if (len == 0)
        return "";

    std::vector<uint8_t> digits((len * 138 / 100) + 1);
    size_t digits_len = 1;
    digits[0] = 0;

    for (size_t i = 0; i < len; ++i) {
        int carry = data[i];

        for (size_t j = 0; j < digits_len; ++j) {
            int value = digits[j] * 256 + carry;
            digits[j] = value % 58;
            carry = value / 58;
        }

        while (carry) {
            digits[digits_len++] = carry % 58;
            carry /= 58;
        }
    }

    size_t zeros = 0;

    while (zeros < len && data[zeros] == 0)
        zeros++;

    std::string out;

    out.reserve(zeros + digits_len);

    for (size_t i = 0; i < zeros; ++i)
        out += '1';

    for (size_t i = digits_len; i > 0; --i)
        out += BASE58[digits[i - 1]];

    return out;
}


/*
 * CUDA kernel:
 * generate random 32-byte Ed25519 seeds.
 *
 * Each thread produces one candidate.
 */
__global__
void generate_candidates(
    uint8_t* output,
    uint64_t seed_base,
    uint64_t count
)
{
    uint64_t idx =
        static_cast<uint64_t>(blockIdx.x) *
        blockDim.x +
        threadIdx.x;

    if (idx >= count)
        return;

    curandStatePhilox4_32_10_t state;

    curand_init(
        seed_base,
        idx,
        0,
        &state
    );

    uint8_t* dst =
        output + idx * SEED_SIZE;

    for (int i = 0; i < SEED_SIZE; i += 4) {

        unsigned int r =
            curand(&state);

        dst[i + 0] = (r >> 0)  & 0xff;
        dst[i + 1] = (r >> 8)  & 0xff;
        dst[i + 2] = (r >> 16) & 0xff;
        dst[i + 3] = (r >> 24) & 0xff;
    }
}


/*
 * Generate Ed25519 public key from 32-byte seed.
 *
 * OpenSSL Ed25519 expects the 32-byte seed.
 */
bool ed25519_public_key(
    const uint8_t seed[32],
    uint8_t pub[32]
)
{
    EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(
        EVP_PKEY_ED25519,
        nullptr,
        seed,
        32
    );

    if (!pkey)
        return false;

    size_t pub_len = 32;

    int rc = EVP_PKEY_get_raw_public_key(
        pkey,
        pub,
        &pub_len
    );

    EVP_PKEY_free(pkey);

    return rc == 1 && pub_len == 32;
}


void save_keypair(
    const uint8_t seed[32],
    const uint8_t pub[32],
    const std::string& filename
)
{
    std::ofstream file(filename);

    if (!file) {
        throw std::runtime_error(
            "Cannot open output file"
        );
    }

    file << "[\n";

    for (int i = 0; i < 32; ++i) {

        file << static_cast<unsigned int>(seed[i])
             << ",\n";
    }

    for (int i = 0; i < 32; ++i) {

        file << static_cast<unsigned int>(pub[i]);

        if (i != 31)
            file << ",";

        file << "\n";
    }

    file << "]\n";

    file.close();
}


void process_batch(
    const uint8_t* seeds,
    uint64_t count,
    const std::string& prefix
)
{
    #pragma omp parallel for schedule(dynamic)
    for (long long i = 0;
         i < static_cast<long long>(count);
         ++i)
    {
        if (found.load(std::memory_order_relaxed))
            continue;

        const uint8_t* seed =
            seeds + i * SEED_SIZE;

        uint8_t pub[32];

        if (!ed25519_public_key(seed, pub))
            continue;

        std::string address =
            base58_encode(pub, 32);

        tested.fetch_add(
            1,
            std::memory_order_relaxed
        );

        if (address.rfind(prefix, 0) == 0) {

            bool expected = false;

            if (found.compare_exchange_strong(expected, true)) {

                std::lock_guard<std::mutex> lock(
                    result_mutex
                );

                memcpy(result.seed, seed, 32);
                memcpy(result.pub, pub, 32);

                result.address = address;
            }
        }
    }
}


int main(int argc, char** argv)
{
    std::string prefix = "miaw";

    if (argc >= 2)
        prefix = argv[1];

    std::cout
        << "Solana CUDA Vanity Generator\n"
        << "Prefix: " << prefix << "\n\n";

    if (prefix.empty()) {
        std::cerr << "Prefix cannot be empty\n";
        return 1;
    }

    /*
     * Batch size.
     *
     * 1 million candidates = 32 MB.
     */
    const uint64_t BATCH =
        1'000'000ULL;

    uint8_t* d_seeds = nullptr;

    cudaError_t err =
        cudaMalloc(
            &d_seeds,
            BATCH * SEED_SIZE
        );

    if (err != cudaSuccess) {

        std::cerr
            << "cudaMalloc failed: "
            << cudaGetErrorString(err)
            << "\n";

        return 1;
    }

    std::vector<uint8_t> h_seeds(
        BATCH * SEED_SIZE
    );

    const int threads = 256;

    const int blocks =
        static_cast<int>(
            (BATCH + threads - 1) /
            threads
        );

    uint64_t batch_id = 0;

    auto start =
        std::chrono::steady_clock::now();

    uint64_t last_report = 0;

    while (!found.load()) {

        uint64_t cuda_seed =
            static_cast<uint64_t>(
                std::chrono::high_resolution_clock::
                now().time_since_epoch().count()
            ) ^
            (++batch_id);

        generate_candidates<<<
            blocks,
            threads
        >>>(
            d_seeds,
            cuda_seed,
            BATCH
        );

        err = cudaDeviceSynchronize();

        if (err != cudaSuccess) {

            std::cerr
                << "CUDA error: "
                << cudaGetErrorString(err)
                << "\n";

            break;
        }

        err = cudaMemcpy(
            h_seeds.data(),
            d_seeds,
            BATCH * SEED_SIZE,
            cudaMemcpyDeviceToHost
        );

        if (err != cudaSuccess) {

            std::cerr
                << "cudaMemcpy failed: "
                << cudaGetErrorString(err)
                << "\n";

            break;
        }

        process_batch(
            h_seeds.data(),
            BATCH,
            prefix
        );

        uint64_t current =
            tested.load();

        if (current - last_report >= 1'000'000) {

            last_report = current;

            auto now =
                std::chrono::steady_clock::now();

            double seconds =
                std::chrono::duration<double>(
                    now - start
                ).count();

            double rate =
                current / seconds;

            std::cout
                << "\rTested: "
                << current
                << " | "
                << std::fixed
                << std::setprecision(2)
                << rate / 1'000'000.0
                << " M keys/s"
                << std::flush;
        }
    }

    cudaFree(d_seeds);

    if (!found.load()) {

        std::cerr
            << "\nGeneration stopped.\n";

        return 1;
    }

    std::cout
        << "\n\nFOUND!\n"
        << "Address: "
        << result.address
        << "\n";

    /*
     * 64-byte Solana keypair:
     *
     * [32-byte secret seed][32-byte public key]
     */
    save_keypair(
        result.seed,
        result.pub,
        "keypair.json"
    );

    std::cout
        << "Saved: keypair.json\n";

    return 0;
}    printf("c[0] = %f\n", h_c[0]);
    printf("c[1023] = %f\n", h_c[1023]);

    cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
    free(h_a); free(h_b); free(h_c);
    return 0;
}

Comments (0)

to post comments and vote
No comments yet. Be the first to share your thoughts!