Got bored and trying to waste time.
Created code to just scan range and convert addresses from it.
Speed is still fair?
Scanned: 8080480016 keys | Speed: 1611.89 Mkeys/s
Example privkey (hex): 000000000000000000000000000000000000000000000002000000003a3daf00
Compressed pubkey: 03957d109cb86e8b1316e1d27e5ecb347e654f13083a444c3c10c3ac7c834e7372
Uncompressed pubkey: 04957d109cb86e8b1316e1d27e5ecb347e654f13083a444c3c10c3ac7c834e7372a841548bce4a3
e38af176b5998944b017bb33695f0db559c15c7c02f7b515221
Compressed BTC Address: 1FkkYxzrE4CQNvk9PhSCsap1MnRpG8cRE
Uncompressed BTC Address: 17vHhKrRDEyZwBQLrgA4vo1XAikHEYtfvN
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <vector>
#include <numeric>
#include <cstring>
#include <cstdint>
#include <algorithm> // added for std::find_if
#include <secp256k1.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>
// Define start and end key range in hex
constexpr const char* RANGE_START = "20000000000000000";
constexpr const char* RANGE_END = "3FFFFFFFFFFFFFFFFF";
// Default thread count
constexpr unsigned int THREAD_COUNT = 8;
// Batch size per iteration
constexpr unsigned int BATCH_SIZE = 16; // deeper unrolling
// Parse a hex string into __uint128_t
static inline __uint128_t parseHex128(const char* hex) {
__uint128_t v = 0;
for (const char* p = hex; *p; ++p) {
char c = *p;
uint8_t nibble = 0;
if (c >= '0' && c <= '9') nibble = c - '0';
else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10;
else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10;
v = (v << 4) | nibble;
}
return v;
}
void uint128ToPrivKey(__uint128_t value, uint8_t out[32]) {
memset(out, 0, 32);
for (int i = 0; i < 16; ++i) {
out[31 - i] = (uint8_t)(value >> (i * 8));
}
}
bool derivePubkey(secp256k1_context* ctx, const uint8_t priv[32], uint8_t out[65], bool compressed) {
secp256k1_pubkey pubkey;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, priv)) return false;
size_t len = compressed ? 33 : 65;
return secp256k1_ec_pubkey_serialize(ctx, out, &len, &pubkey,
compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
}
std::string toHex(const uint8_t* data, size_t len) {
static const char hex_digits[] = "0123456789abcdef";
std::string out;
out.reserve(len * 2);
for (size_t i = 0; i < len; ++i) {
out.push_back(hex_digits[data[i] >> 4]);
out.push_back(hex_digits[data[i] & 0xF]);
}
return out;
}
const char* BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
std::string base58Encode(const std::vector<uint8_t>& input) {
std::vector<uint8_t> temp(input);
std::string result;
size_t zeroCount = 0;
while (zeroCount < temp.size() && temp[zeroCount] == 0) {
++zeroCount;
}
std::vector<uint8_t> b58((temp.size() - zeroCount) * 138 / 100 + 1);
size_t length = 0;
for (size_t i = zeroCount; i < temp.size(); ++i) {
int carry = temp[i];
size_t j = 0;
for (ssize_t k = b58.size() - 1; (carry != 0 || j < length) && k >= 0; --k, ++j) {
carry += 256 * b58[k];
b58[k] = carry % 58;
carry /= 58;
}
length = j;
}
auto it = std::find_if(b58.begin(), b58.end(), [](uint8_t c) { return c != 0; });
for (size_t i = 0; i < zeroCount; ++i) result += '1';
for (; it != b58.end(); ++it) result += BASE58_ALPHABET[*it];
return result;
}
std::string pubkeyToAddress(const uint8_t* pubkey, size_t len) {
uint8_t sha256[SHA256_DIGEST_LENGTH];
SHA256(pubkey, len, sha256);
uint8_t ripemd[RIPEMD160_DIGEST_LENGTH];
RIPEMD160(sha256, SHA256_DIGEST_LENGTH, ripemd);
std::vector<uint8_t> addressBytes = {0x00};
addressBytes.insert(addressBytes.end(), ripemd, ripemd + RIPEMD160_DIGEST_LENGTH);
uint8_t hash1[SHA256_DIGEST_LENGTH];
SHA256(addressBytes.data(), addressBytes.size(), hash1);
uint8_t hash2[SHA256_DIGEST_LENGTH];
SHA256(hash1, SHA256_DIGEST_LENGTH, hash2);
addressBytes.insert(addressBytes.end(), hash2, hash2 + 4);
return base58Encode(addressBytes);
}
int main() {
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
__uint128_t start = parseHex128(RANGE_START);
__uint128_t end = parseHex128(RANGE_END);
__uint128_t total_range = end - start + 1;
__uint128_t delta = total_range / THREAD_COUNT;
alignas(64) uint64_t counters[THREAD_COUNT] = {0};
alignas(64) __uint128_t lastScannedKey[THREAD_COUNT] = {0};
auto tStart = std::chrono::steady_clock::now();
auto worker = [&](int id, __uint128_t chunkStart, __uint128_t chunkEnd) {
__uint128_t cur = chunkStart;
uint64_t local = 0;
while (cur <= chunkEnd) {
// #pragma unroll 16
for (unsigned i = 0; i < BATCH_SIZE && cur <= chunkEnd; ++i) {
++cur;
++local;
}
counters[id] += local;
lastScannedKey[id] = cur;
local = 0;
}
};
std::vector<std::thread> pool;
pool.reserve(THREAD_COUNT);
for (unsigned i = 0; i < THREAD_COUNT; ++i) {
__uint128_t chunkStart = start + delta * i;
__uint128_t chunkEnd = (i + 1 == THREAD_COUNT) ? end : (chunkStart + delta - 1);
pool.emplace_back(worker, i, chunkStart, chunkEnd);
}
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(5));
auto tNow = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration<double>(tNow - tStart).count();
uint64_t total = std::accumulate(counters, counters + THREAD_COUNT, uint64_t(0));
double speed = double(total) / elapsed / 1e6;
uint8_t priv[32];
uint8_t pubCompressed[33];
uint8_t pubUncompressed[65];
uint128ToPrivKey(lastScannedKey[0], priv);
std::string hexPriv = toHex(priv, 32);
std::string hexPubCompressed = "[invalid]";
std::string hexPubUncompressed = "[invalid]";
std::string addressCompressed = "[invalid]";
std::string addressUncompressed = "[invalid]";
if (derivePubkey(ctx, priv, pubCompressed, true)) {
hexPubCompressed = toHex(pubCompressed, 33);
addressCompressed = pubkeyToAddress(pubCompressed, 33);
}
if (derivePubkey(ctx, priv, pubUncompressed, false)) {
hexPubUncompressed = toHex(pubUncompressed, 65);
addressUncompressed = pubkeyToAddress(pubUncompressed, 65);
}
std::cout << "Scanned: " << total << " keys | Speed: "
<< std::fixed << std::setprecision(2) << speed << " Mkeys/s\n"
<< "Example privkey (hex): " << hexPriv << "\n"
<< "Compressed pubkey: " << hexPubCompressed << "\n"
<< "Uncompressed pubkey: " << hexPubUncompressed << "\n"
<< "Compressed BTC Address: " << addressCompressed << "\n"
<< "Uncompressed BTC Address: " << addressUncompressed << "\n";
}
for (auto &t : pool) t.join();
secp256k1_context_destroy(ctx);
return 0;
}
When compare results against targets.txt speed gets fucked.
Have no idea how to improve it!
Loaded 3 target addresses.
Initial scan example (starting from RANGE_START):
Privkey: 0000000000000000000000000000000000000000000000020000000000000000
Compressed pubkey: 028d26200250cebdae120ef31b04c80cd50d4cddc8eadbcf29fc696d32c0ade462
Compressed BTC Address: 1LgpDjsqkxF9cTkz3UYSbdTJuvbZ45PKvx
MATCH FOUND (Compressed):
Privkey: 000000000000000000000000000000000000000000000002000000000000e690
Address: 1H3o2oVZApFkzp3DnoDjg8FCGXFtmBNBz5
[Speed] Scanned: 813040 keys | 0.15 Mkeys/s
[Speed] Scanned: 1559888 keys | 0.15 Mkeys/s
[Speed] Scanned: 2278496 keys | 0.15 Mkeys/s
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <vector>
#include <numeric>
#include <cstring>
#include <cstdint>
#include <algorithm>
#include <unordered_set>
#include <fstream>
#include <mutex>
#include <secp256k1.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>
constexpr const char* RANGE_START = "20000000000000000";
constexpr const char* RANGE_END = "3FFFFFFFFFFFFFFFFF";
constexpr unsigned int THREAD_COUNT = 8;
constexpr unsigned int BATCH_SIZE = 16;
std::mutex printMutex;
static inline __uint128_t parseHex128(const char* hex) {
__uint128_t v = 0;
for (const char* p = hex; *p; ++p) {
char c = *p;
uint8_t nibble = 0;
if (c >= '0' && c <= '9') nibble = c - '0';
else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10;
else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10;
v = (v << 4) | nibble;
}
return v;
}
void uint128ToPrivKey(__uint128_t value, uint8_t out[32]) {
memset(out, 0, 32);
for (int i = 0; i < 16; ++i) {
out[31 - i] = (uint8_t)(value >> (i * 8));
}
}
bool derivePubkey(secp256k1_context* ctx, const uint8_t priv[32], uint8_t out[65], bool compressed) {
secp256k1_pubkey pubkey;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, priv)) return false;
size_t len = compressed ? 33 : 65;
return secp256k1_ec_pubkey_serialize(ctx, out, &len, &pubkey,
compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
}
std::string toHex(const uint8_t* data, size_t len) {
static const char hex_digits[] = "0123456789abcdef";
std::string out;
out.reserve(len * 2);
for (size_t i = 0; i < len; ++i) {
out.push_back(hex_digits[data[i] >> 4]);
out.push_back(hex_digits[data[i] & 0xF]);
}
return out;
}
const char* BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
std::string base58Encode(const std::vector<uint8_t>& input) {
std::vector<uint8_t> temp(input);
std::string result;
size_t zeroCount = 0;
while (zeroCount < temp.size() && temp[zeroCount] == 0) {
++zeroCount;
}
std::vector<uint8_t> b58((temp.size() - zeroCount) * 138 / 100 + 1);
size_t length = 0;
for (size_t i = zeroCount; i < temp.size(); ++i) {
int carry = temp[i];
size_t j = 0;
for (ssize_t k = b58.size() - 1; (carry != 0 || j < length) && k >= 0; --k, ++j) {
carry += 256 * b58[k];
b58[k] = carry % 58;
carry /= 58;
}
length = j;
}
auto it = std::find_if(b58.begin(), b58.end(), [](uint8_t c) { return c != 0; });
for (size_t i = 0; i < zeroCount; ++i) result += '1';
for (; it != b58.end(); ++it) result += BASE58_ALPHABET[*it];
return result;
}
std::string pubkeyToAddress(const uint8_t* pubkey, size_t len) {
uint8_t sha256[SHA256_DIGEST_LENGTH];
SHA256(pubkey, len, sha256);
uint8_t ripemd[RIPEMD160_DIGEST_LENGTH];
RIPEMD160(sha256, SHA256_DIGEST_LENGTH, ripemd);
std::vector<uint8_t> addressBytes = {0x00};
addressBytes.insert(addressBytes.end(), ripemd, ripemd + RIPEMD160_DIGEST_LENGTH);
uint8_t hash1[SHA256_DIGEST_LENGTH];
SHA256(addressBytes.data(), addressBytes.size(), hash1);
uint8_t hash2[SHA256_DIGEST_LENGTH];
SHA256(hash1, SHA256_DIGEST_LENGTH, hash2);
addressBytes.insert(addressBytes.end(), hash2, hash2 + 4);
return base58Encode(addressBytes);
}
int main() {
std::unordered_set<std::string> targetAddresses;
std::ifstream infile("targets.txt");
std::string line;
while (std::getline(infile, line)) {
if (!line.empty()) targetAddresses.insert(line);
}
std::cout << "Loaded " << targetAddresses.size() << " target addresses.\n";
std::ofstream foundFile("found.txt", std::ios::app);
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
__uint128_t start = parseHex128(RANGE_START);
__uint128_t end = parseHex128(RANGE_END);
__uint128_t total_range = end - start + 1;
__uint128_t delta = total_range / THREAD_COUNT;
alignas(64) uint64_t counters[THREAD_COUNT] = {0};
alignas(64) __uint128_t lastScannedKey[THREAD_COUNT] = {0};
auto tStart = std::chrono::steady_clock::now();
auto worker = [&](int id, __uint128_t chunkStart, __uint128_t chunkEnd) {
uint8_t priv[32], pubCompressed[33];
__uint128_t cur = chunkStart;
while (cur <= chunkEnd) {
for (unsigned i = 0; i < BATCH_SIZE && cur <= chunkEnd; ++i, ++cur) {
uint128ToPrivKey(cur, priv);
std::string hexPriv = toHex(priv, 32);
if (derivePubkey(ctx, priv, pubCompressed, true)) {
std::string addr = pubkeyToAddress(pubCompressed, 33);
if (targetAddresses.count(addr)) {
std::lock_guard<std::mutex> lock(printMutex);
std::cout << "\nMATCH FOUND (Compressed):\nPrivkey: " << hexPriv << "\nAddress: " << addr << "\n";
foundFile << "Compressed:\nPrivkey: " << hexPriv << "\nAddress: " << addr << "\n\n";
foundFile.flush();
}
}
}
counters[id] += BATCH_SIZE;
lastScannedKey[id] = cur;
}
};
std::vector<std::thread> pool;
pool.reserve(THREAD_COUNT);
for (unsigned i = 0; i < THREAD_COUNT; ++i) {
__uint128_t chunkStart = start + delta * i;
__uint128_t chunkEnd = (i + 1 == THREAD_COUNT) ? end : (chunkStart + delta - 1);
pool.emplace_back(worker, i, chunkStart, chunkEnd);
}
// Show the true first key (from RANGE_START)
__uint128_t firstKey = parseHex128(RANGE_START);
uint8_t priv[32], pubCompressed[33];
uint128ToPrivKey(firstKey, priv);
std::string hexPriv = toHex(priv, 32);
std::string hexPubCompressed = "[invalid]";
std::string addressCompressed = "[invalid]";
if (derivePubkey(ctx, priv, pubCompressed, true)) {
hexPubCompressed = toHex(pubCompressed, 33);
addressCompressed = pubkeyToAddress(pubCompressed, 33);
}
std::cout << "Initial scan example (starting from RANGE_START):\n"
<< "Privkey: " << hexPriv << "\n"
<< "Compressed pubkey: " << hexPubCompressed << "\n"
<< "Compressed BTC Address: " << addressCompressed << "\n";
std::thread monitor([&]() {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(5));
auto tNow = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration<double>(tNow - tStart).count();
uint64_t total = std::accumulate(counters, counters + THREAD_COUNT, uint64_t(0));
double speed = double(total) / elapsed / 1e6;
std::lock_guard<std::mutex> lock(printMutex);
std::cout << "[Speed] Scanned: " << total << " keys | "
<< std::fixed << std::setprecision(2) << speed << " Mkeys/s\n";
}
});
for (auto &t : pool) t.join();
monitor.detach();
secp256k1_context_destroy(ctx);
return 0;
}