Maybe someone will find my idea attractive and implement it, thereby speeding up finding solutions to puzzles!
Ok...Here is simplest starting code for this.
sudo apt update && sudo apt upgrade -y && sudo apt install -y build-essential libssl-dev libboost-all-dev openssl libsecp256k1-dev git cmake
mutagen.cpp
#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <queue>
#include <mutex>
#include <unordered_map>
#include <cmath>
#include <fstream>
#include <openssl/ec.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <openssl/bn.h>
#include <openssl/obj_mac.h>
#include <openssl/evp.h>
using namespace std;
// Configuration
const string TARGET_HASH160 = "b907c3a2a3b27789dfb509b730dd47703c272868";
const string BASE_KEY = "000000000000000000000000000000000000000000000000000000000005749f";
const int PUZZLE_NUM = 20;
const int WORKERS = thread::hardware_concurrency();
// Historical flip counts
const unordered_map<int, int> FLIP_TABLE = {
{20, 8}, {21, 9}, {22, 11}, {23, 12}, {24, 9}, {25, 12}, {26, 14}, {27, 13},
{28, 16}, {29, 18}, {30, 16}, {31, 13}, {32, 14}, {33, 15}, {34, 16}, {35, 19},
{36, 14}, {37, 23}, {38, 21}, {39, 23}, {40, 20}, {41, 25}, {42, 24}, {43, 19},
{44, 24}, {45, 21}, {46, 24}, {47, 27}, {48, 21}, {49, 30}, {50, 29}, {51, 25},
{52, 27}, {53, 26}, {54, 30}, {55, 31}, {56, 31}, {57, 33}, {58, 28}, {59, 30},
{60, 31}, {61, 25}, {62, 35}, {63, 34}, {64, 34}, {65, 37}, {66, 35}, {67, 31},
{68, 34}
};
// Global variables
atomic<bool> stop_event(false);
mutex result_mutex;
queue<tuple<string, size_t, int>> results;
// Predict flip count
int predict_flips(int puzzle_num) {
if (FLIP_TABLE.count(puzzle_num)) {
return FLIP_TABLE.at(puzzle_num);
}
vector<int> x, y;
for (const auto& [key, value] : FLIP_TABLE) {
x.push_back(key);
y.push_back(value);
}
double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
for (size_t i = 0; i < x.size(); ++i) {
sum_x += x[i];
sum_y += y[i];
sum_xy += x[i] * y[i];
sum_xx += x[i] * x[i];
}
double slope = (x.size() * sum_xy - sum_x * sum_y) / (x.size() * sum_xx - sum_x * sum_x);
double intercept = (sum_y - slope * sum_x) / x.size();
int predicted = round(slope * puzzle_num + intercept);
int bit_length = puzzle_num;
int lower = max(8, static_cast<int>(bit_length * 0.5));
int upper = min(static_cast<int>(bit_length * 0.6), bit_length - 5);
return min(max(predicted, lower), upper);
}
// Binomial coefficient
size_t combinations_count(int n, int k) {
if (k > n) return 0;
size_t result = 1;
for (int i = 1; i <= k; ++i) {
result *= (n - i + 1);
result /= i;
}
return result;
}
// Generate combinations
vector<vector<int>> generate_combinations(int n, int k) {
vector<vector<int>> combinations;
vector<int> current(k, 0);
for (int i = 0; i < k; ++i) current[i] = i;
while (true) {
combinations.push_back(current);
int i = k - 1;
while (i >= 0 && current[i] == n - k + i) --i;
if (i < 0) break;
++current[i];
for (int j = i + 1; j < k; ++j) current[j] = current[j - 1] + 1;
}
return combinations;
}
// Mutate key
uint64_t mutate_key(uint64_t base_int, const vector<int>& flip_positions) {
uint64_t flip_mask = 0;
for (int pos : flip_positions) flip_mask |= (1ULL << pos);
return base_int ^ flip_mask;
}
// Compute HASH160
string compute_hash160(EC_KEY* ec_key) {
const EC_GROUP* group = EC_KEY_get0_group(ec_key);
const EC_POINT* pub_key = EC_KEY_get0_public_key(ec_key);
if (!group || !pub_key) {
cerr << "Invalid EC key components" << endl;
return "";
}
size_t pub_key_len = EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_COMPRESSED, nullptr, 0, nullptr);
if (pub_key_len == 0) {
cerr << "Failed to get public key length" << endl;
return "";
}
vector<unsigned char> pub_key_bytes(pub_key_len);
if (!EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_COMPRESSED, pub_key_bytes.data(), pub_key_len, nullptr)) {
cerr << "Failed to serialize public key" << endl;
return "";
}
unsigned char sha256_result[SHA256_DIGEST_LENGTH];
SHA256(pub_key_bytes.data(), pub_key_bytes.size(), sha256_result);
unsigned char ripemd160_result[RIPEMD160_DIGEST_LENGTH];
RIPEMD160(sha256_result, SHA256_DIGEST_LENGTH, ripemd160_result);
stringstream ss;
for (int i = 0; i < RIPEMD160_DIGEST_LENGTH; ++i) {
ss << hex << setw(2) << setfill('0') << static_cast<int>(ripemd160_result[i]);
}
return ss.str();
}
// Worker function
void worker(uint64_t base_int, int bit_length, int flip_count, size_t start_index, size_t end_index) {
size_t checked = 0;
auto combinations = generate_combinations(bit_length, flip_count);
EC_KEY* ec_key = EC_KEY_new();
if (!ec_key) {
cerr << "Failed to create EC_KEY" << endl;
return;
}
EC_GROUP* ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!ec_group) {
cerr << "Failed to create EC_GROUP (secp256k1 not available?)" << endl;
EC_KEY_free(ec_key);
return;
}
if (!EC_KEY_set_group(ec_key, ec_group)) {
cerr << "Failed to set EC group" << endl;
EC_GROUP_free(ec_group);
EC_KEY_free(ec_key);
return;
}
for (size_t i = start_index; i < end_index && i < combinations.size(); ++i) {
if (stop_event.load()) break;
const auto& flip_pos = combinations[i];
uint64_t priv_int = mutate_key(base_int, flip_pos);
++checked;
BIGNUM* bn_priv = BN_new();
if (!bn_priv) {
cerr << "Failed to create BIGNUM" << endl;
continue;
}
if (!BN_set_word(bn_priv, priv_int)) {
cerr << "Failed to set private key value" << endl;
BN_free(bn_priv);
continue;
}
if (!EC_KEY_set_private_key(ec_key, bn_priv)) {
cerr << "Failed to set private key" << endl;
BN_free(bn_priv);
continue;
}
EC_POINT* pub_key = EC_POINT_new(ec_group);
if (!pub_key) {
BN_free(bn_priv);
continue;
}
if (!EC_POINT_mul(ec_group, pub_key, bn_priv, nullptr, nullptr, nullptr)) {
EC_POINT_free(pub_key);
BN_free(bn_priv);
continue;
}
if (!EC_KEY_set_public_key(ec_key, pub_key)) {
EC_POINT_free(pub_key);
BN_free(bn_priv);
continue;
}
string hash160 = compute_hash160(ec_key);
EC_POINT_free(pub_key);
BN_free(bn_priv);
if (hash160.empty()) {
continue;
}
if (hash160 == TARGET_HASH160) {
lock_guard<mutex> lock(result_mutex);
results.push(make_tuple(to_string(priv_int), checked, flip_count));
stop_event.store(true);
break;
}
if (checked % 10000 == 0) {
cout << "Checked " << checked << "\r";
cout.flush();
}
}
EC_GROUP_free(ec_group);
EC_KEY_free(ec_key);
}
// Parallel search
void parallel_search() {
int bit_length = PUZZLE_NUM;
uint64_t base_int = stoull(BASE_KEY, nullptr, 16);
int flip_count = predict_flips(PUZZLE_NUM);
size_t total_combs = combinations_count(bit_length, flip_count);
cout << "Searching Puzzle " << PUZZLE_NUM << " (256-bit)" << endl;
cout << "Base Key: " << BASE_KEY << endl;
cout << "Target HASH160: " << TARGET_HASH160 << endl;
cout << "Predicted Flip Count: " << flip_count << " bits" << endl;
cout << "Total Possible Combinations: " << total_combs << endl;
cout << "Using " << WORKERS << " workers..." << endl;
vector<thread> threads;
size_t chunk_size = total_combs / WORKERS;
auto start_time = chrono::high_resolution_clock::now();
for (int i = 0; i < WORKERS; ++i) {
size_t start_index = i * chunk_size;
size_t end_index = (i == WORKERS - 1) ? total_combs : (i + 1) * chunk_size;
threads.emplace_back(worker, base_int, bit_length, flip_count, start_index, end_index);
}
for (auto& t : threads) t.join();
if (!results.empty()) {
auto [hex_key, checked, actual_flips] = results.front();
auto elapsed = chrono::duration_cast<chrono::seconds>(chrono::high_resolution_clock::now() - start_time).count();
cout << "\nFound in " << elapsed / 3600.0 << " hours" << endl;
cout << "Private Key: " << hex_key << endl;
cout << "Actual Bit Flips: " << actual_flips << endl;
cout << "Keys Checked: " << checked << endl;
cout << "Speed: " << checked / static_cast<double>(elapsed) << " keys/sec" << endl;
ofstream solution_file("puzzle_" + to_string(PUZZLE_NUM) + "_solution.txt");
solution_file << hex_key;
cout << "\nSolution saved to puzzle_" << PUZZLE_NUM << "_solution.txt" << endl;
} else {
cout << "\nKey not found. Try adjusting flip count ±2" << endl;
}
}
int main() {
// Initialize OpenSSL (modern versions don't need this, but we'll include it for compatibility)
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OpenSSL_add_all_algorithms();
#endif
int flip_count = predict_flips(PUZZLE_NUM);
cout << "Predicted flip count for " << PUZZLE_NUM << ": " << flip_count << " bits" << endl;
parallel_search();
// Clean up OpenSSL (only needed for older versions)
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_cleanup();
#endif
return 0;
}
g++ -O3 -march=native -std=c++17 mutagen.cpp -lssl -lcrypto -lpthread -o mutagen
# ./mutagen
Predicted flip count for 20: 8 bits
Searching Puzzle 20 (256-bit)
Base Key: 000000000000000000000000000000000000000000000000000000000005749f
Target HASH160: b907c3a2a3b27789dfb509b730dd47703c272868
Predicted Flip Count: 8 bits
Total Possible Combinations: 125970
Using 12 workers...
Found in 0.000833333 hours
Private Key: 863317
Actual Bit Flips: 8
Keys Checked: 5167
Speed: 1722.33 keys/sec
Solution saved to puzzle_20_solution.txt
If you want better, you have to do everything yourself from here.

I understand that I need to do it myself, but still, thank you very much for the prototype)