It's not possible to simply snap your fingers and create a script out of thin air.
I think programmers are magicians. They wave a magic wand and solve puzzles.

Can you share some code?

git clone https://github.com/Dookoo2/Cyclone.git
Cyclone.cpp
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <cmath>
#include <chrono>
#include <cstring>
#include <algorithm>
#include <omp.h>
#include <random>
#include <immintrin.h>
#include <array>
#include <mutex>
// Adding program modules
#include "sha256_avx2.h"
#include "ripemd160_avx2.h"
#include "SECP256K1.h"
#include "Point.h"
#include "Int.h"
#include "IntGroup.h"
#define BISIZE 256
#if BISIZE == 256
#define NB64BLOCK 5
#define NB32BLOCK 10
#else
#error Unsupported size
#endif
// Constants
static constexpr int POINTS_BATCH_SIZE = 256;
static constexpr int HASH_BATCH_SIZE = 8;
int g_prefixLength = 4; // Default prefix length
// Status output and progress saving frequency
static constexpr double statusIntervalSec = 5.0;
static constexpr double saveProgressIntervalSec = 300.0;
static int g_progressSaveCount = 0;
static std::vector<std::string> g_threadPrivateKeys;
// Mutex for thread-safe printing
std::mutex coutMutex;
//------------------------------------------------------------------------------
void saveProgressToFile(const std::string &progressStr) {
std::ofstream ofs("progress.txt", std::ios::app);
if (ofs) {
ofs << progressStr << "\n";
} else {
std::cerr << "Cannot open progress.txt for writing\n";
}
}
//------------------------------------------------------------------------------
// Converts a HEX string into a large number (a vector of 64-bit words, little-endian).
std::vector<uint64_t> hexToBigNum(const std::string &hex) {
std::vector<uint64_t> bigNum;
const size_t len = hex.size();
bigNum.reserve((len + 15) / 16);
for (size_t i = 0; i < len; i += 16) {
size_t start = (len >= 16 + i) ? len - 16 - i : 0;
size_t partLen = (len >= 16 + i) ? 16 : (len - i);
uint64_t value = std::stoull(hex.substr(start, partLen), nullptr, 16);
bigNum.push_back(value);
}
return bigNum;
}
// Reverse conversion to a HEX string (with correct leading zeros within blocks).
std::string bigNumToHex(const std::vector<uint64_t> &num) {
std::ostringstream oss;
for (auto it = num.rbegin(); it != num.rend(); ++it) {
if (it != num.rbegin())
oss << std::setw(16) << std::setfill('0');
oss << std::hex << *it;
}
return oss.str();
}
std::vector<uint64_t> singleElementVector(uint64_t val) {
return {val};
}
std::vector<uint64_t> bigNumAdd(const std::vector<uint64_t> &a, const std::vector<uint64_t> &b) {
std::vector<uint64_t> sum;
sum.reserve(std::max(a.size(), b.size()) + 1);
uint64_t carry = 0;
for (size_t i = 0, sz = std::max(a.size(), b.size()); i < sz; ++i) {
uint64_t x = (i < a.size()) ? a[i] : 0ULL;
uint64_t y = (i < b.size()) ? b[i] : 0ULL;
__uint128_t s = (__uint128_t)x + (__uint128_t)y + carry;
carry = (uint64_t)(s >> 64);
sum.push_back((uint64_t)s);
}
if (carry) sum.push_back(carry);
return sum;
}
std::vector<uint64_t> bigNumSubtract(const std::vector<uint64_t> &a, const std::vector<uint64_t> &b) {
std::vector<uint64_t> diff = a;
uint64_t borrow = 0;
for (size_t i = 0; i < b.size(); ++i) {
uint64_t subtrahend = b[i];
if (diff[i] < subtrahend + borrow) {
diff[i] = diff[i] + (~0ULL) - subtrahend - borrow + 1ULL; // eqv diff[i] = diff[i] - subtrahend - borrow
borrow = 1ULL;
} else {
diff[i] -= (subtrahend + borrow);
borrow = 0ULL;
}
}
for (size_t i = b.size(); i < diff.size() && borrow; ++i) {
if (diff[i] == 0ULL) {
diff[i] = ~0ULL;
} else {
diff[i] -= 1ULL;
borrow = 0ULL;
}
}
// delete leading zeros
while (!diff.empty() && diff.back() == 0ULL)
diff.pop_back();
return diff;
}
std::pair<std::vector<uint64_t>, uint64_t> bigNumDivide(const std::vector<uint64_t> &a, uint64_t divisor) {
std::vector<uint64_t> quotient(a.size(), 0ULL);
uint64_t remainder = 0ULL;
for (int i = (int)a.size() - 1; i >= 0; --i) {
__uint128_t temp = ((__uint128_t)remainder << 64) | a[i];
uint64_t q = (uint64_t)(temp / divisor);
uint64_t r = (uint64_t)(temp % divisor);
quotient[i] = q;
remainder = r;
}
while (!quotient.empty() && quotient.back() == 0ULL)
quotient.pop_back();
return {quotient, remainder};
}
long double hexStrToLongDouble(const std::string &hex) {
long double result = 0.0L;
for (char c : hex) {
result *= 16.0L;
if (c >= '0' && c <= '9')
result += (c - '0');
else if (c >= 'a' && c <= 'f')
result += (c - 'a' + 10);
else if (c >= 'A' && c <= 'F')
result += (c - 'A' + 10);
}
return result;
}
//------------------------------------------------------------------------------
static inline std::string padHexTo64(const std::string &hex) {
return (hex.size() >= 64) ? hex : std::string(64 - hex.size(), '0') + hex;
}
static inline Int hexToInt(const std::string &hex) {
Int number;
char buf[65] = {0};
std::strncpy(buf, hex.c_str(), 64);
number.SetBase16(buf);
return number;
}
static inline std::string intToHex(const Int &value) {
Int temp;
temp.Set((Int *)&value);
return temp.GetBase16();
}
static inline bool intGreater(const Int &a, const Int &b) {
std::string ha = ((Int &)a).GetBase16();
std::string hb = ((Int &)b).GetBase16();
if (ha.size() != hb.size()) return (ha.size() > hb.size());
return (ha > hb);
}
static inline bool isEven(const Int &number) {
return ((Int &)number).IsEven();
}
static inline std::string intXToHex64(const Int &x) {
Int temp;
temp.Set((Int *)&x);
std::string hex = temp.GetBase16();
if (hex.size() < 64)
hex.insert(0, 64 - hex.size(), '0');
return hex;
}
static inline std::string pointToCompressedHex(const Point &point) {
return (isEven(point.y) ? "02" : "03") + intXToHex64(point.x);
}
static inline void pointToCompressedBin(const Point &point, uint8_t outCompressed[33]) {
outCompressed[0] = isEven(point.y) ? 0x02 : 0x03;
Int temp;
temp.Set((Int *)&point.x);
for (int i = 0; i < 32; i++) {
outCompressed[1 + i] = (uint8_t)temp.GetByte(31 - i);
}
}
//------------------------------------------------------------------------------
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);
}
// Computing hash160 using avx2 (8 hashes per try)
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);
for (size_t i = 0; i < batchCount; i++) {
const size_t idx = batch * HASH_BATCH_SIZE + i;
prepareShaBlock(pubKeys[idx], 33, shaInputs[i].data());
}
for (size_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
std::memcpy(shaInputs[i].data(), shaInputs[0].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();
}
// SHA256 (avx2)
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]);
// Preparing Ripemd160
for (size_t i = 0; i < batchCount; i++) {
prepareRipemdBlock(shaOutputs[i].data(), ripemdInputs[i].data());
}
for (size_t i = batchCount; i < HASH_BATCH_SIZE; i++) {
std::memcpy(ripemdInputs[i].data(), ripemdInputs[0].data(), 64);
}
for (int i = 0; i < HASH_BATCH_SIZE; i++) {
inPtr[i] = ripemdInputs[i].data();
outPtr[i] = ripemdOutputs[i].data();
}
// Ripemd160 (avx2)
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++) {
const size_t idx = batch * HASH_BATCH_SIZE + i;
std::memcpy(hashResults[idx], ripemdOutputs[i].data(), 20);
}
}
}
//------------------------------------------------------------------------------
static void printUsage(const char *programName) {
std::cerr << "Usage: " << programName << " -h <hash160_hex> [-p <puzzle> | -r <startHex:endHex>] -b <prefix_length>\n";
}
static std::string formatElapsedTime(double seconds) {
int hrs = (int)seconds / 3600;
int mins = ((int)seconds % 3600) / 60;
int secs = (int)seconds % 60;
std::ostringstream oss;
oss << std::setw(2) << std::setfill('0') << hrs << ":"
<< std::setw(2) << std::setfill('0') << mins << ":"
<< std::setw(2) << std::setfill('0') << secs;
return oss.str();
}
//------------------------------------------------------------------------------
// Function to generate partial match information
std::string generatePartialMatchInfo(const std::string& privateKeyHex, const std::string& publicKeyHex,
const uint8_t* foundHash160, const uint8_t* targetHash160, int prefixLength) {
std::ostringstream oss;
oss << "================== PARTIAL MATCH FOUND! ============\n";
oss << "Prefix length : " << prefixLength << " bytes\n";
oss << "Private Key : " << privateKeyHex << "\n";
oss << "Public Key : " << publicKeyHex << "\n";
oss << "Found Hash160 : ";
for (int b = 0; b < 20; b++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)foundHash160[b];
}
oss << "\n";
oss << "Target Hash160: ";
for (int b = 0; b < 20; b++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)targetHash160[b];
}
oss << "\n";
oss << "Matched bytes : ";
for (int b = 0; b < prefixLength; b++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)targetHash160[b];
}
oss << std::endl;
return oss.str();
}
// Function to print status block and partial match information
static void printStatsBlock(int numCPUs, const std::string &targetHash160Hex,
const std::string &rangeStr, double mkeysPerSec,
unsigned long long totalChecked, double elapsedTime,
int puzzle, const std::string& partialMatchInfo = "") {
std::lock_guard<std::mutex> lock(coutMutex);
// Move cursor to the top-left corner
std::cout << "\033[2J\033[1;1H\r";
// Print the status block (lines 1-8)
std::cout << "================= WORK IN PROGRESS =================\n";
std::cout << "Puzzle : " << puzzle << "\n"; // Print puzzle value
std::cout << "Range : " << rangeStr << "\n";
std::cout << "Target Hash160: " << targetHash160Hex << "\n";
std::cout << "CPU Threads : " << numCPUs << "\n";
std::cout << "Mkeys/s : " << std::fixed << std::setprecision(2) << mkeysPerSec << "\n";
std::cout << "Total Checked : " << totalChecked << "\n";
std::cout << "Elapsed Time : " << formatElapsedTime(elapsedTime) << "\n";
// Print the partial match information (lines 10-15)
if (!partialMatchInfo.empty()) {
// Move cursor to line 10
std::cout << "\033[10;1H";
// Clear the line
std::cout << "\033[K";
std::cout << partialMatchInfo;
// Save partial match to MATCH.txt
std::ofstream matchFile("MATCH.txt", std::ios::app);
if (matchFile) {
matchFile << partialMatchInfo << "\n";
} else {
std::cerr << "Cannot open MATCH.txt for writing\n";
}
}
std::cout.flush();
}
//------------------------------------------------------------------------------
struct ThreadRange {
std::string startHex;
std::string endHex;
};
static std::vector<ThreadRange> g_threadRanges;
class Timer {
public:
static std::string getSeed(int length) {
auto now = std::chrono::high_resolution_clock::now();
auto epoch = now.time_since_epoch();
auto value = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch).count();
std::ostringstream oss;
oss << std::hex << value;
return oss.str().substr(0, length);
}
};
class Xoshiro256plus {
public:
Xoshiro256plus(uint64_t seed = 0) {
state[0] = seed;
for (int i = 1; i < 4; ++i) {
state[i] = 1812433253ULL * (state[i - 1] ^ (state[i - 1] >> 30)) + i;
}
}
uint64_t next() {
const uint64_t result = state[0] + state[3];
const uint64_t t = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= t;
state[3] = rotl(state[3], 45);
return result;
}
private:
static inline uint64_t rotl(const uint64_t x, int k) {
return (x << k) | (x >> (64 - k));
}
std::array<uint64_t, 4> state;
};
Int generateRandomPrivateKey(Int minKey, Int range, Xoshiro256plus &rng) {
Int randomPrivateKey((uint64_t)0);
// Generate random values in chunks of 64 bits using Xoshiro256plus
for (int i = 0; i < NB64BLOCK; ++i) {
uint64_t randVal = rng.next();
randomPrivateKey.ShiftL(64); // Shift left by 64 bits
randomPrivateKey.Add(randVal);
}
// Apply modulo operation and add minKey
randomPrivateKey.Mod(&range);
randomPrivateKey.Add(&minKey);
return randomPrivateKey;
}
Int minKey, maxKey;
int main(int argc, char *argv[]) {
bool hash160Provided = false, rangeProvided = false, puzzleProvided = false;
std::string targetHash160Hex;
std::vector<uint8_t> targetHash160;
int puzzle = 0; // Declare puzzle variable
std::string rangeStartHex, rangeEndHex;
// Parse command-line arguments
for (int i = 1; i < argc; i++) {
if (!std::strcmp(argv[i], "-h") && i + 1 < argc) { // Use -h for hash160_hex
targetHash160Hex = argv[++i];
hash160Provided = true;
// Convert the hex string to a byte array
targetHash160.resize(20);
for (size_t j = 0; j < 20; j++) {
targetHash160[j] = std::stoul(targetHash160Hex.substr(j * 2, 2), nullptr, 16);
}
} else if (!std::strcmp(argv[i], "-p") && i + 1 < argc) {
puzzle = std::stoi(argv[++i]);
if (puzzle <= 0) {
std::cerr << "Invalid puzzle value. Must be greater than 0.\n";
return 1;
}
puzzleProvided = true;
} else if (!std::strcmp(argv[i], "-r") && i + 1 < argc) {
std::string range = argv[++i];
size_t colonPos = range.find(':');
if (colonPos == std::string::npos) {
std::cerr << "Invalid range format. Expected startHex:endHex.\n";
return 1;
}
rangeStartHex = range.substr(0, colonPos);
rangeEndHex = range.substr(colonPos + 1);
rangeProvided = true;
} else if (!std::strcmp(argv[i], "-b") && i + 1 < argc) {
g_prefixLength = std::stoi(argv[++i]);
if (g_prefixLength <= 0 || g_prefixLength > 20) {
std::cerr << "Invalid prefix length. Must be between 1 and 20.\n";
return 1;
}
} else {
std::cerr << "Unknown parameter: " << argv[i] << "\n";
printUsage(argv[0]);
return 1;
}
}
if (!hash160Provided || (!rangeProvided && !puzzleProvided)) {
std::cerr << "Both -h and (-p or -r) are required!\n";
printUsage(argv[0]);
return 1;
}
if (puzzleProvided) {
// Calculate the range based on the puzzle number
Int one;
one.SetBase10(const_cast<char *>("1"));
minKey = one;
minKey.ShiftL(puzzle - 1); // Start of range: 2^(puzzle-1)
maxKey = one;
maxKey.ShiftL(puzzle); // End of range: 2^puzzle
maxKey.Sub(&one); // End of range: 2^puzzle - 1
// Convert minKey and maxKey to hex strings for rangeStartHex and rangeEndHex
rangeStartHex = intToHex(minKey);
rangeEndHex = intToHex(maxKey);
} else if (rangeProvided) {
// Calculate puzzle number based on the range
Int rangeStart = hexToInt(rangeStartHex);
Int rangeEnd = hexToInt(rangeEndHex);
Int rangeSize;
rangeSize.Sub(&rangeEnd, &rangeStart);
rangeSize.AddOne();
// Calculate the number of bits required to represent the range size
int bits = 0;
Int temp;
temp.Set(&rangeSize);
while (!temp.IsZero()) {
temp.ShiftR(1);
bits++;
}
puzzle = bits;
}
// Convert range to big numbers
auto rangeStart = hexToBigNum(rangeStartHex);
auto rangeEnd = hexToBigNum(rangeEndHex);
// Validate range
bool validRange = false;
if (rangeStart.size() < rangeEnd.size()) {
validRange = true;
} else if (rangeStart.size() > rangeEnd.size()) {
validRange = false;
} else {
validRange = true;
for (int i = (int)rangeStart.size() - 1; i >= 0; --i) {
if (rangeStart[i] < rangeEnd[i]) {
break;
} else if (rangeStart[i] > rangeEnd[i]) {
validRange = false;
break;
}
}
}
if (!validRange) {
std::cerr << "Range start must be less than range end.\n";
return 1;
}
auto rangeSize = bigNumSubtract(rangeEnd, rangeStart);
rangeSize = bigNumAdd(rangeSize, singleElementVector(1ULL));
const std::string rangeSizeHex = bigNumToHex(rangeSize);
const long double totalRangeLD = hexStrToLongDouble(rangeSizeHex);
const int numCPUs = omp_get_num_procs();
g_threadPrivateKeys.resize(numCPUs, "0");
auto [chunkSize, remainder] = bigNumDivide(rangeSize, (uint64_t)numCPUs);
g_threadRanges.resize(numCPUs);
std::vector<uint64_t> currentStart = rangeStart;
for (int t = 0; t < numCPUs; t++) {
auto currentEnd = bigNumAdd(currentStart, chunkSize);
if (t < (int)remainder) {
currentEnd = bigNumAdd(currentEnd, singleElementVector(1ULL));
}
currentEnd = bigNumSubtract(currentEnd, singleElementVector(1ULL));
g_threadRanges[t].startHex = bigNumToHex(currentStart);
g_threadRanges[t].endHex = bigNumToHex(currentEnd);
currentStart = bigNumAdd(currentEnd, singleElementVector(1ULL));
}
const std::string displayRange = g_threadRanges.front().startHex + ":" + g_threadRanges.back().endHex;
unsigned long long globalComparedCount = 0ULL;
double globalElapsedTime = 0.0;
double mkeysPerSec = 0.0;
const auto tStart = std::chrono::high_resolution_clock::now();
auto lastStatusTime = tStart;
auto lastSaveTime = tStart;
bool matchFound = false;
std::string foundPrivateKeyHex;
std::string foundPublicKeyHex; // Declare foundPublicKeyHex
Int one;
one.SetBase10(const_cast<char *>("1"));
Int minKey = one;
minKey.ShiftL(puzzle - 1); // Start of range: 2^(puzzle-1)
Int maxKey = one;
maxKey.ShiftL(puzzle); // End of range: 2^puzzle - 1
maxKey.Sub(&one);
Int range = maxKey;
range.Sub(&minKey);
Secp256K1 secp;
secp.Init();
// PARRALEL COMPUTING BLOCK
#pragma omp parallel num_threads(numCPUs) \
shared(globalComparedCount, globalElapsedTime, mkeysPerSec, matchFound, \
foundPrivateKeyHex, foundPublicKeyHex, lastStatusTime, lastSaveTime, g_progressSaveCount, \
g_threadPrivateKeys)
{
const int threadId = omp_get_thread_num();
// Initialize Xoshiro256plus PRNG for this thread
Xoshiro256plus rng(std::chrono::steady_clock::now().time_since_epoch().count() + threadId);
Int privateKey = hexToInt(g_threadRanges[threadId].startHex);
const Int threadRangeEnd = hexToInt(g_threadRanges[threadId].endHex);
#pragma omp critical
{
g_threadPrivateKeys[threadId] = padHexTo64(intToHex(privateKey));
}
// Precomputing +i*G and -i*G for i=0..255
std::vector<Point> plusPoints(POINTS_BATCH_SIZE);
std::vector<Point> minusPoints(POINTS_BATCH_SIZE);
for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
Int tmp;
tmp.SetInt32(i);
Point p = secp.ComputePublicKey(&tmp);
plusPoints[i] = p;
p.y.ModNeg();
minusPoints[i] = p;
}
// Arrays for batch-adding
std::vector<Int> deltaX(POINTS_BATCH_SIZE);
IntGroup modGroup(POINTS_BATCH_SIZE);
// Save 512 publickeys
const int fullBatchSize = 2 * POINTS_BATCH_SIZE;
std::vector<Point> pointBatch(fullBatchSize);
// Buffers for hashing
uint8_t localPubKeys[fullBatchSize][33];
uint8_t localHashResults[HASH_BATCH_SIZE][20];
int localBatchCount = 0;
int pointIndices[HASH_BATCH_SIZE];
// Local count
unsigned long long localComparedCount = 0ULL;
// Download the target (hash160) в __m128i for fast compare
__m128i target16 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(targetHash160.data()));
// Main loop for generating random private keys
while (!matchFound) {
// Generate a random private key within the thread's range using Xoshiro256plus
Int currentBatchKey = generateRandomPrivateKey(minKey, range, rng);
currentBatchKey.Set(&privateKey);
Point startPoint = secp.ComputePublicKey(¤tBatchKey);
#pragma omp critical
{
g_threadPrivateKeys[threadId] = padHexTo64(intToHex(privateKey));
}
// Divide the batch of 512 keys into 2 blocks of 256 keys, count +256 and -256 from the center G-point of the batch
// First pointBatch[0..255] +
for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
deltaX[i].ModSub(&plusPoints[i].x, &startPoint.x);
}
modGroup.Set(deltaX.data());
modGroup.ModInv();
for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
Point tempPoint = startPoint;
Int deltaY;
deltaY.ModSub(&plusPoints[i].y, &startPoint.y);
Int slope;
slope.ModMulK1(&deltaY, &deltaX[i]);
Int slopeSq;
slopeSq.ModSquareK1(&slope);
Int tmpX;
tmpX.Set(&startPoint.x);
tmpX.ModNeg();
tmpX.ModAdd(&slopeSq);
tmpX.ModSub(&plusPoints[i].x);
tempPoint.x.Set(&tmpX);
Int diffX;
diffX.Set(&startPoint.x);
diffX.ModSub(&tempPoint.x);
diffX.ModMulK1(&slope);
tempPoint.y.ModNeg();
tempPoint.y.ModAdd(&diffX);
pointBatch[i] = tempPoint;
}
// Second pointBatch[256..511] -
for (int i = 0; i < POINTS_BATCH_SIZE; i++) {
Point tempPoint = startPoint;
Int deltaY;
deltaY.ModSub(&minusPoints[i].y, &startPoint.y);
Int slope;
slope.ModMulK1(&deltaY, &deltaX[i]);
Int slopeSq;
slopeSq.ModSquareK1(&slope);
Int tmpX;
tmpX.Set(&startPoint.x);
tmpX.ModNeg();
tmpX.ModAdd(&slopeSq);
tmpX.ModSub(&minusPoints[i].x);
tempPoint.x.Set(&tmpX);
Int diffX;
diffX.Set(&startPoint.x);
diffX.ModSub(&tempPoint.x);
diffX.ModMulK1(&slope);
tempPoint.y.ModNeg();
tempPoint.y.ModAdd(&diffX);
pointBatch[POINTS_BATCH_SIZE + i] = tempPoint;
}
// Construct local buffer
for (int i = 0; i < fullBatchSize; i++) {
pointToCompressedBin(pointBatch[i], localPubKeys[localBatchCount]);
pointIndices[localBatchCount] = i;
localBatchCount++;
// 8 keys are ready - time to use avx2
if (localBatchCount == HASH_BATCH_SIZE) {
computeHash160BatchBinSingle(localBatchCount, localPubKeys, localHashResults);
// Results check
for (int j = 0; j < HASH_BATCH_SIZE; j++) {
__m128i cand16 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(localHashResults[j]));
__m128i cmp = _mm_cmpeq_epi8(cand16, target16);
uint16_t bitmask = (0xFFFF >> (16 - 4 * g_prefixLength)) << (16 - 4 * g_prefixLength);
// Check for full match
if ((_mm_movemask_epi8(cmp) & bitmask) == bitmask) {
if (!matchFound && std::memcmp(localHashResults[j], targetHash160.data(), 20) == 0) {
#pragma omp critical
{
if (!matchFound) {
matchFound = true;
auto tEndTime = std::chrono::high_resolution_clock::now();
globalElapsedTime = std::chrono::duration<double>(tEndTime - tStart).count();
mkeysPerSec = (double)(globalComparedCount + localComparedCount) / globalElapsedTime / 1e6;
// Recovering private key
Int matchingPrivateKey;
matchingPrivateKey.Set(¤tBatchKey);
int idx = pointIndices[j];
if (idx < 256) {
Int offset;
offset.SetInt32(idx);
matchingPrivateKey.Add(&offset);
} else {
Int offset;
offset.SetInt32(idx - 256);
matchingPrivateKey.Sub(&offset);
}
foundPrivateKeyHex = padHexTo64(intToHex(matchingPrivateKey));
Point matchedPoint = pointBatch[idx];
foundPublicKeyHex = pointToCompressedHex(matchedPoint); // Assign foundPublicKeyHex
// Save full match to KEYFOUND.txt
std::ofstream keyFoundFile("KEYFOUND.txt", std::ios::app);
if (keyFoundFile) {
keyFoundFile << "================== FULL MATCH FOUND! ==================\n";
keyFoundFile << "Private Key : " << foundPrivateKeyHex << "\n";
keyFoundFile << "Public Key : " << foundPublicKeyHex << "\n";
keyFoundFile << "Total Checked : " << globalComparedCount << "\n";
keyFoundFile << "Elapsed Time : " << formatElapsedTime(globalElapsedTime) << "\n";
keyFoundFile << "Speed : " << mkeysPerSec << " Mkeys/s\n";
} else {
std::cerr << "Cannot open KEYFOUND.txt for writing\n";
}
}
}
#pragma omp cancel parallel
}
localComparedCount++;
}
// Check for partial match (independent of full match)
bool bytesMatch = true;
for (int b = 0; b < g_prefixLength; b++) {
if (localHashResults[j][b] != targetHash160.data()[b]) {
bytesMatch = false;
break;
}
}
if (bytesMatch) {
// Recovering private key for partial match
Int matchingPrivateKey;
matchingPrivateKey.Set(¤tBatchKey);
int idx = pointIndices[j];
if (idx < 256) {
Int offset;
offset.SetInt32(idx);
matchingPrivateKey.Add(&offset);
} else {
Int offset;
offset.SetInt32(idx - 256);
matchingPrivateKey.Sub(&offset);
}
foundPrivateKeyHex = padHexTo64(intToHex(matchingPrivateKey));
Point matchedPoint = pointBatch[idx];
foundPublicKeyHex = pointToCompressedHex(matchedPoint); // Assign foundPublicKeyHex
// Generate partial match information
std::string partialMatchInfo = generatePartialMatchInfo(foundPrivateKeyHex, foundPublicKeyHex,
localHashResults[j], targetHash160.data(), g_prefixLength);
// Print status block and partial match information
printStatsBlock(numCPUs, targetHash160Hex, displayRange,
mkeysPerSec, globalComparedCount,
globalElapsedTime, puzzle, partialMatchInfo);
}
localComparedCount++;
}
localBatchCount = 0;
}
}
// Next step
{
Int step;
step.SetInt32(fullBatchSize - 2); // 510
privateKey.Add(&step);
}
// Time to show status
auto now = std::chrono::high_resolution_clock::now();
double secondsSinceStatus = std::chrono::duration<double>(now - lastStatusTime).count();
if (secondsSinceStatus >= statusIntervalSec) {
#pragma omp critical
{
globalComparedCount += localComparedCount;
localComparedCount = 0ULL;
globalElapsedTime = std::chrono::duration<double>(now - tStart).count();
mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
// Print status block without partial match information
printStatsBlock(numCPUs, targetHash160Hex, displayRange,
mkeysPerSec, globalComparedCount,
globalElapsedTime, puzzle);
lastStatusTime = now;
}
}
if (matchFound) {
break;
}
} // while(true)
// Adding local count
#pragma omp atomic
globalComparedCount += localComparedCount;
} // end of parallel section
// Main results
auto tEnd = std::chrono::high_resolution_clock::now();
globalElapsedTime = std::chrono::duration<double>(tEnd - tStart).count();
if (!matchFound) {
mkeysPerSec = (double)globalComparedCount / globalElapsedTime / 1e6;
std::cout << "\nNo match found.\n";
std::cout << "Total Checked : " << globalComparedCount << "\n";
std::cout << "Elapsed Time : " << formatElapsedTime(globalElapsedTime) << "\n";
std::cout << "Speed : " << mkeysPerSec << " Mkeys/s\n";
return 0;
}
// If the key was found
std::cout << "================== FULL MATCH FOUND! ==================\n";
std::cout << "Private Key : " << foundPrivateKeyHex << "\n";
std::cout << "Total Checked : " << globalComparedCount << "\n";
std::cout << "Elapsed Time : " << formatElapsedTime(globalElapsedTime) << "\n";
std::cout << "Speed : " << mkeysPerSec << " Mkeys/s\n";
return 0;
}
Makefile
# Compiler
CXX = g++
# Compiler flags
CXXFLAGS = -m64 -std=c++17 -Ofast -mssse3 -Wall -Wextra \
-Wno-write-strings -Wno-unused-variable -Wno-deprecated-copy \
-Wno-unused-parameter -Wno-sign-compare -Wno-strict-aliasing \
-Wno-unused-but-set-variable \
-march=native -mtune=native \
-funroll-loops -ftree-vectorize -fstrict-aliasing -fno-semantic-interposition \
-fvect-cost-model=unlimited -fno-trapping-math -fipa-ra -flto \
-fassociative-math -fopenmp -mavx2 -mbmi2 -madx \
# Source files
SRCS = Cyclone.cpp SECP256K1.cpp Int.cpp Timer.cpp IntGroup.cpp IntMod.cpp \
Point.cpp ripemd160_avx2.cpp sha256_avx2.cpp
# Object files
OBJS = $(SRCS:.cpp=.o)
# Target executable
TARGET = Cyclone
# Default target
all: fix_rdtsc $(TARGET)
# Target to replace __rdtsc with my_rdtsc
fix_rdtsc:
find . -type f -name '*.cpp' -exec sed -i 's/__rdtsc/my_rdtsc/g' {} +
# Link the object files to create the executable and then delete .o files
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
rm -f $(OBJS) && chmod +x $(TARGET)
# Compile each source file into an object file
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Clean up build files
clean:
echo "Cleaning..."
rm -f $(OBJS) $(TARGET)
# Phony targets
.PHONY: all clean fix_rdtsc
You can use:
./Cyclone -h bfebb73562d4541b32a02ba664d140b5a574792f -r 2000000:3ffffff -b 8
or
./Cyclone -h bfebb73562d4541b32a02ba664d140b5a574792f -p 26 -b 8
================= WORK IN PROGRESS =================
Puzzle : 26
Range : 2000000:3ffffff
Target Hash160: bfebb73562d4541b32a02ba664d140b5a574792f
CPU Threads : 12
Mkeys/s : 95.93
Total Checked : 5720064
Elapsed Time : 00:00:00
================== PARTIAL MATCH FOUND! ============
Prefix length : 8 bytes
Private Key : 000000000000000000000000000000000000000000000000000000000340326E
Public Key : 024E4F50A2A3ECCDB368988AE37CD4B611697B26B29696E42E06D71368B4F3840F
Found Hash160 : bfebb73562d4541b32a02ba664d140b5a574792f
Target Hash160: bfebb73562d4541b32a02ba664d140b5a574792f
Matched bytes : bfebb73562d4541b
================== FULL MATCH FOUND! ==================
Private Key : 000000000000000000000000000000000000000000000000000000000340326E
Total Checked : 33885184
Elapsed Time : 00:00:00
Speed : 95.93 Mkeys/s
Partial Matches: Saved to MATCH.txt when the first g_prefixLength bytes of the hash match.
Full Matches: Saved to KEYFOUND.txt when the entire hash matches.