nomachine
|
 |
June 04, 2025, 09:40:14 AM Last edit: June 04, 2025, 10:51:08 AM by nomachine |
|
Who wrote this code?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Share your donation address I can see light at the end of a tunnel.
I wrote it. It's based on the lightweight database for publickeys from @Mcdouglas-X3. The difference is that my script empties RAM and stores the DB in parts compressed on disk. You need a huge amount of storage. And of course the public key. Here is C++ version #include <iostream> #include <atomic> #include <memory> #include <fstream> #include <string> #include <cstdlib> #include <vector> #include <unordered_map> #include <cmath> #include <gmpxx.h> #include <chrono> #include <cassert> #include <cstdio> #include <sys/stat.h> #include <xxhash.h> #include <omp.h> #include <unistd.h> #include <iomanip>
using namespace std;
// Constants and typedefs typedef array<mpz_class, 2> Point;
const mpz_class modulo("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16); const mpz_class order("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); const mpz_class Gx("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16); const mpz_class Gy("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16); const Point PG = {Gx, Gy}; const Point Z = {0, 0};
// Global variables int puzzle = 40; string puzzle_pubkey = "03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4"; int threads = 0; bool verbose = false; const size_t MAX_TABLE_SIZE = 200 * 1024 * 1024; // 200MB per part
// Function declarations void print_help(); bool validate_pubkey(const string& pubkey); Point parse_pubkey(const string& pubkey); inline Point add(const Point& P, const Point& Q, const mpz_class& p = modulo); inline Point mul(const mpz_class& k, const Point& P = PG, const mpz_class& p = modulo); inline Point point_subtraction(const Point& P, const Point& Q); inline mpz_class X2Y(const mpz_class& X, int y_parity, const mpz_class& p = modulo); inline string point_to_cpub(const Point& point); inline string hash_cpub(const string& cpub); void save_baby_table_part(const unordered_map<string, int>& baby_table, int part_num); void save_baby_table(const unordered_map<string, int>& baby_table); unordered_map<string, int> load_baby_table_part(const string& filename); unordered_map<string, int> load_baby_table(); void delete_existing_table(); unordered_map<string, int> generate_baby_table_parallel(const mpz_class& m);
void print_help() { cout << "BSGS (Baby-Step Giant-Step) Elliptic Curve Cryptography Tool\n\n"; cout << "Usage: ./bsgs [options]\n\n"; cout << "Options:\n"; cout << " -p <number> Puzzle number (default: 40)\n"; cout << " -k <pubkey> Compressed public key in hex format\n"; cout << " -t <threads> Number of CPU cores to use (default: all available)\n"; cout << " -v Verbose output\n"; cout << " -h Show this help message\n\n"; cout << "Example:\n"; cout << " ./bsgs -p 40 -k 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4 -t 8\n"; }
bool validate_pubkey(const string& pubkey) { if (pubkey.length() != 66) { cerr << "[error] Public key must be 66 characters long (including 02/03 prefix)\n"; return false; } if (pubkey[0] != '0' || (pubkey[1] != '2' && pubkey[1] != '3')) { cerr << "[error] Public key must start with 02 or 03\n"; return false; } for (size_t i = 2; i < pubkey.length(); i++) { if (!isxdigit(pubkey[i])) { cerr << "[error] Public key contains invalid hex characters\n"; return false; } } return true; }
Point parse_pubkey(const string& pubkey) { string prefix = pubkey.substr(0, 2); mpz_class X(pubkey.substr(2), 16); int y_parity = stoi(prefix) - 2; mpz_class Y = X2Y(X, y_parity); if (Y == 0) { cerr << "[error] Invalid compressed public key!\n"; exit(1); } return {X, Y}; }
inline Point add(const Point& P, const Point& Q, const mpz_class& p) { if (P == Z) return Q; if (Q == Z) return P; const mpz_class& P0 = P[0]; const mpz_class& P1 = P[1]; const mpz_class& Q0 = Q[0]; const mpz_class& Q1 = Q[1]; mpz_class lmbda, num, denom, inv; if (P != Q) { num = Q1 - P1; denom = Q0 - P0; } else { if (P1 == 0) return Z; num = 3 * P0 * P0; denom = 2 * P1; } mpz_invert(inv.get_mpz_t(), denom.get_mpz_t(), p.get_mpz_t()); lmbda = (num * inv) % p; mpz_class x = (lmbda * lmbda - P0 - Q0) % p; if (x < 0) x += p; mpz_class y = (lmbda * (P0 - x) - P1) % p; if (y < 0) y += p; return {x, y}; }
inline Point mul(const mpz_class& k, const Point& P, const mpz_class& p) { Point R0 = Z, R1 = P; unsigned long bit_length = mpz_sizeinbase(k.get_mpz_t(), 2); for (unsigned long i = bit_length - 1; i < bit_length; --i) { if (mpz_tstbit(k.get_mpz_t(), i)) { R0 = add(R0, R1, p); R1 = add(R1, R1, p); } else { R1 = add(R0, R1, p); R0 = add(R0, R0, p); } } return R0; }
inline Point point_subtraction(const Point& P, const Point& Q) { Point Q_neg = {Q[0], (-Q[1]) % modulo}; return add(P, Q_neg); }
inline mpz_class X2Y(const mpz_class& X, int y_parity, const mpz_class& p) { mpz_class X_cubed = (X * X * X) % p; mpz_class tmp = (X_cubed + mpz_class(7)) % p; mpz_class Y; mpz_class exp = (p + mpz_class(1)) / mpz_class(4); mpz_powm(Y.get_mpz_t(), tmp.get_mpz_t(), exp.get_mpz_t(), p.get_mpz_t()); if ((Y % 2) != y_parity) { Y = p - Y; } return Y; }
inline string point_to_cpub(const Point& point) { mpz_class x = point[0], y = point[1]; int y_parity = y.get_ui() & 1; string prefix = y_parity == 0 ? "02" : "03"; char buffer[65]; mpz_get_str(buffer, 16, x.get_mpz_t()); return prefix + string(buffer); }
inline string hash_cpub(const string& cpub) { XXH64_hash_t hash = XXH64(cpub.c_str(), cpub.size(), 0); char buffer[17]; snprintf(buffer, sizeof(buffer), "%016lx", hash); return string(buffer, 8); }
void save_baby_table_part(const unordered_map<string, int>& baby_table, int part_num) { string filename = "baby_table_part_" + to_string(part_num); ofstream outfile(filename, ios::binary); if (!outfile) { cerr << "[error] Failed to open file for writing: " << filename << endl; return; } for (const auto& entry : baby_table) { outfile.write(entry.first.c_str(), 8); int index = entry.second; outfile.write(reinterpret_cast<const char*>(&index), sizeof(index)); } if (verbose) { cout << "[+] Saved baby table part " << part_num << " with " << baby_table.size() << " entries" << endl; } string command = "pigz -9 -b 128 -f " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to compress file: " << filename << endl; return; } pclose(pipe); }
void save_baby_table(const unordered_map<string, int>& baby_table) { size_t current_size = 0; int part_num = 1; unordered_map<string, int> current_part; for (const auto& entry : baby_table) { size_t entry_size = entry.first.size() + sizeof(int); if (current_size + entry_size > MAX_TABLE_SIZE && !current_part.empty()) { save_baby_table_part(current_part, part_num++); current_part.clear(); current_size = 0; } current_part.insert(entry); current_size += entry_size; } if (!current_part.empty()) { save_baby_table_part(current_part, part_num); } }
unordered_map<string, int> load_baby_table_part(const string& filename) { string command = "pigz -d -c " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to decompress file: " << filename << endl; return {}; } unordered_map<string, int> baby_table_part; char key_buffer[9]; int index; while (fread(key_buffer, 8, 1, pipe) == 1) { key_buffer[8] = '\0'; fread(&index, sizeof(index), 1, pipe); baby_table_part[key_buffer] = index; } pclose(pipe); return baby_table_part; }
unordered_map<string, int> load_baby_table() { unordered_map<string, int> baby_table; int part_num = 1; while (true) { string filename = "baby_table_part_" + to_string(part_num) + ".gz"; ifstream test(filename); if (!test.good()) { if (part_num == 1) { cerr << "[error] No baby table parts found!" << endl; return {}; } break; } test.close(); string command = "pigz -d -c " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to decompress file: " << filename << endl; return {}; } char key[9] = {0}; int index; size_t entries = 0; while (fread(key, 8, 1, pipe) == 1) { if (fread(&index, sizeof(int), 1, pipe) != 1) break; key[8] = '\0'; baby_table[key] = index; entries++; } pclose(pipe); if (verbose) { cout << "[+] Loaded part " << part_num << " with " << entries << " entries" << endl; } part_num++; } cout << "[+] Loaded baby table with " << baby_table.size() << " total entries" << endl; return baby_table; }
void delete_existing_table() { int part_num = 1; while (true) { string filename = "baby_table_part_" + to_string(part_num); string compressed_filename = filename + ".gz"; struct stat buffer; bool found = false; if (stat(filename.c_str(), &buffer) == 0) { if (remove(filename.c_str()) != 0) { cerr << "[error] Failed to delete file: " << filename << endl; } else { found = true; } } if (stat(compressed_filename.c_str(), &buffer) == 0) { if (remove(compressed_filename.c_str()) != 0) { cerr << "[error] Failed to delete file: " << compressed_filename << endl; } else { found = true; } } if (!found) { if (part_num == 1 && verbose) { cout << "[+] No existing table parts found to delete" << endl; } break; } part_num++; } }
unordered_map<string, int> generate_baby_table_parallel(const mpz_class& m) { const int num_threads = threads > 0 ? threads : omp_get_max_threads(); const unsigned long total_entries = m.get_ui(); const size_t max_part_size = MAX_TABLE_SIZE * 0.99; // 99% threshold std::atomic<int> parts_created(0); std::atomic<size_t> current_part_size(0); std::atomic<size_t> total_entries_written(0); system("rm -f baby_table_part_* baby_table_part_*.gz 2>/dev/null"); cout << "[+] Generating " << total_entries << " baby steps" << endl;
#pragma omp parallel num_threads(num_threads) { vector<pair<string, int>> local_buffer; local_buffer.reserve(100000);
#pragma omp for schedule(dynamic, 100000) for (unsigned long i = 0; i < total_entries; ++i) { Point P = mul(i); string cpub_hash = hash_cpub(point_to_cpub(P)); local_buffer.emplace_back(cpub_hash, i);
if (local_buffer.size() >= 100000) { #pragma omp critical { int current_part = parts_created + 1; string filename = "baby_table_part_" + to_string(current_part); ofstream outfile(filename, ios::binary | ios::app); if (outfile) { for (const auto& entry : local_buffer) { outfile.write(entry.first.c_str(), 8); outfile.write(reinterpret_cast<const char*>(&entry.second), sizeof(int)); total_entries_written++; current_part_size += 12; if (current_part_size >= max_part_size) { outfile.close(); string cmd = "pigz -9 -b 128 -f " + filename; system(cmd.c_str()); parts_created++; current_part_size = 0; } } } local_buffer.clear(); } } }
#pragma omp critical { if (!local_buffer.empty()) { int current_part = parts_created + 1; string filename = "baby_table_part_" + to_string(current_part); ofstream outfile(filename, ios::binary | ios::app); if (outfile) { for (const auto& entry : local_buffer) { outfile.write(entry.first.c_str(), 8); outfile.write(reinterpret_cast<const char*>(&entry.second), sizeof(int)); total_entries_written++; } outfile.close(); string cmd = "pigz -9 -b 128 -f " + filename; system(cmd.c_str()); parts_created++; } } } }
cout << "[+] Generated " << parts_created << " compressed parts (" << total_entries_written << " total entries) " << endl;
return {}; }
int main(int argc, char* argv[]) { // Parse command line arguments int opt; while ((opt = getopt(argc, argv, "p:k:t:vh")) != -1) { switch (opt) { case 'p': puzzle = atoi(optarg); if (puzzle < 1 || puzzle > 256) { cerr << "[error] Invalid puzzle number (must be between 1-256)\n"; print_help(); return 1; } break; case 'k': puzzle_pubkey = optarg; if (!validate_pubkey(puzzle_pubkey)) { print_help(); return 1; } break; case 't': threads = atoi(optarg); if (threads < 1) { cerr << "[error] Thread count must be at least 1\n"; print_help(); return 1; } break; case 'v': verbose = true; break; case 'h': default: print_help(); return 0; } }
// Initialize OpenMP threads if (threads > 0) { omp_set_num_threads(threads); } int actual_threads = omp_get_max_threads();
// Print configuration time_t currentTime = time(nullptr); cout << "\n\033[01;33m[+]\033[32m BSGS Started: \033[01;33m" << ctime(¤tTime); cout << "\033[0m[+] Puzzle: " << puzzle << endl; cout << "[+] Public Key: " << puzzle_pubkey << endl; cout << "[+] Using " << actual_threads << " CPU cores" << endl;
// Initialize range and point mpz_class start_range = mpz_class(1) << (puzzle - 1); mpz_class end_range = (mpz_class(1) << puzzle) - 1; Point P = parse_pubkey(puzzle_pubkey);
// Calculate m and generate baby table mpz_class m = sqrt(end_range - start_range); m = m * 4; Point m_P = mul(m);
if (verbose) { cout << "[+] Range: 2^" << (puzzle-1) << " to 2^" << puzzle << "-1" << endl; cout << "[+] Baby-step count (m): " << m << endl; }
delete_existing_table();
cout << "[+] Generating baby table..." << endl; auto baby_table = generate_baby_table_parallel(m);
cout << "[+] Loading baby table..." << endl; baby_table = load_baby_table(); if (baby_table.empty()) { cerr << "[error] Failed to load baby table\n"; return 1; }
cout << "[+] Starting BSGS search..." << endl; Point S = point_subtraction(P, mul(start_range)); mpz_class step = 0; bool found = false; mpz_class found_key; auto st = chrono::high_resolution_clock::now(); #pragma omp parallel { Point local_S = S; mpz_class local_step = step; #pragma omp for schedule(dynamic) for (int i = 0; i < actual_threads; ++i) { while (local_step < (end_range - start_range)) { #pragma omp flush(found) if (found) break; string cpub = point_to_cpub(local_S); string cpub_hash = hash_cpub(cpub); auto it = baby_table.find(cpub_hash); if (it != baby_table.end()) { int b = it->second; mpz_class k = start_range + local_step + b; if (point_to_cpub(mul(k)) == puzzle_pubkey) { #pragma omp critical { if (!found) { found = true; found_key = k; auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;32m[+] Solution found!\033[0m" << endl; cout << "[+] Private key: " << k << endl; cout << "[+] Hex: 0x" << hex << k << dec << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; } } #pragma omp flush(found) break; } } local_S = point_subtraction(local_S, m_P); local_step += m; } } }
if (!found) { auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;31m[!] Key not found in the specified range\033[0m" << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; }
return 0; } Makefile # Detect OS UNAME_S := $(shell uname -s)
# Enable static linking by default (change to 'no' to use dynamic linking) STATIC_LINKING = yes
# Compiler settings based on OS ifeq ($(UNAME_S),Linux) # Linux settings
# Compiler CXX = g++
# Compiler flags CXXFLAGS = -m64 -std=c++17 -march=native -mtune=native -Ofast -mssse3 -Wall -Wextra \ -funroll-loops -ftree-vectorize -fstrict-aliasing -fno-semantic-interposition \ -fvect-cost-model=unlimited -fno-trapping-math -fipa-ra -flto -fopenmp \ -mavx2 -mbmi2 -madx LDFLAGS = -lgmp -lgmpxx -lxxhash
# Source files SRCS = bsgs.cpp
# Object files OBJS = $(SRCS:.cpp=.o)
# Target executable TARGET = bsgs
# Default target all: $(TARGET)
# Link the object files to create the executable $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) 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: all clean
else # Windows settings (MinGW-w64)
# Compiler CXX = g++
# Check if compiler is found CHECK_COMPILER := $(shell which $(CXX) 2>/dev/null)
# Add MSYS path if the compiler is not found ifeq ($(CHECK_COMPILER),) $(info Compiler not found. Adding MSYS path to the environment...) SHELL := cmd PATH := C:\msys64\mingw64\bin;$(PATH) endif
# Compiler flags CXXFLAGS = -m64 -std=c++17 -march=native -mtune=native -Ofast -mssse3 -Wall -Wextra \ -funroll-loops -ftree-vectorize -fstrict-aliasing -fno-semantic-interposition \ -fvect-cost-model=unlimited -fno-trapping-math -fipa-ra -flto -fopenmp \ -mavx2 -mbmi2 -madx LDFLAGS = -lgmp -lgmpxx -lxxhash
# Add -static flag if STATIC_LINKING is enabled ifeq ($(STATIC_LINKING),yes) LDFLAGS += -static else $(info Dynamic linking will be used. Ensure required DLLs are distributed) endif
# Source files SRCS = bsgs.cpp
# Object files OBJS = $(SRCS:.cpp=.o)
# Target executable TARGET = bsgs.exe
# Default target all: $(TARGET)
# Link the object files to create the executable $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) del /q $(OBJS) 2>nul
# Compile each source file into an object file %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@
# Clean up build files clean: @echo Cleaning... del /q $(OBJS) $(TARGET) 2>nul
.PHONY: all clean endif dependencies: sudo apt install libgmp-dev libomp-dev xxhash pigz - BSGS Started: Wed Jun 4 01:56:48 2025
- Puzzle: 40
- Public Key: 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4
- Using 12 CPU cores
- Generating baby table...
- Generating 2965820 baby steps
- Generated 1 compressed parts (2965820 total entries)
- Loading baby table...
- Loaded baby table with 2964816 total entries
- Starting BSGS search...
- Solution found!
- Private key: 1003651412950
- Hex: 0xe9ae4933d6
- Time elapsed: 0.612888 seconds
# ./bsgs -p 50 -k 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6 -t 12 - BSGS Started: Wed Jun 4 02:20:52 2025
- Puzzle: 50
- Public Key: 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6
- Using 12 CPU cores
- Generating baby table...
- Generating 94906264 baby steps
- Generated 6 compressed parts (94906264 total entries)
- Loading baby table...
- Loaded baby table with 93398236 total entries
- Starting BSGS search...
- Solution found!
- Private key: 611140496167764
- Hex: 0x22bd43c2e9354
- Time elapsed: 4.83157 seconds
P.S. This can be easily modified to use a Bloom Filter. And probably the GPU version. Use it for whatever you want.
|
BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
|
|
|
kTimesG
|
 |
June 04, 2025, 10:55:40 AM Last edit: June 04, 2025, 11:10:48 AM by kTimesG |
|
The most amusing stuff using mutex locks and creating bloomfilters with the same inputs two times in row. alexander@alexander-home:~/Documents/Test_Dir/Point_Search_GMP$ diff bloom1B.bf bloom1.bf Binary files bloom1B.bf and bloom1.bf differ
What did you expect? I looked at your update, and you are simply creating multiple mutexes, one for each thread that runs process_chunk. And locking the entire loop. Basically protecting nothing. That's not mutexes are for. You only need a single mutex, and you only need to lock the "bf.insert" call, not the entire loop (or else the entire loops will be exclusive). I'd personally move the mutex to the bloom filter code, and further block only the actual code that accesses data which can potentially be shared (for example, the hashing part probably doesn't need exclusive access). But I'm glad that at least you got to a case where you can clearly see that the output is wrong, when synchronization is missing. So which one of those 2 outputs is the right one? You'll never know, since they were basically in a race condition, running both in parallel under different mutexes (so, identical as not having a mutex at all). If you wanna go fancy you can implement a multi-mutex scheme, one for each some memory area size, and only lock the specific mutex for the area the bloom filter writes. This may increase throughput, or it may not, the right balance needs to be found by trial and error. But this is not a programming thread, after all.  LE: another option is to compute the points in parallel, and queue them in a producer-consumer fashion. And consuming the queue in a single thread, that only does the BF insertions. This simply moves the sync on the queue itself, of course, if you don't want to mess with the bloom class.
|
Off the grid, training pigeons to broadcast signed messages.
|
|
|
AlexanderCurl
Jr. Member
Offline
Activity: 33
Merit: 171
|
 |
June 04, 2025, 12:55:38 PM Last edit: June 04, 2025, 02:08:19 PM by AlexanderCurl |
|
The most amusing stuff using mutex locks and creating bloomfilters with the same inputs two times in row. alexander@alexander-home:~/Documents/Test_Dir/Point_Search_GMP$ diff bloom1B.bf bloom1.bf Binary files bloom1B.bf and bloom1.bf differ
What did you expect? I looked at your update, and you are simply creating multiple mutexes, one for each thread that runs process_chunk. And locking the entire loop. Basically protecting nothing. That's not mutexes are for. You only need a single mutex, and you only need to lock the "bf.insert" call, not the entire loop (or else the entire loops will be exclusive). I'd personally move the mutex to the bloom filter code, and further block only the actual code that accesses data which can potentially be shared (for example, the hashing part probably doesn't need exclusive access). But I'm glad that at least you got to a case where you can clearly see that the output is wrong, when synchronization is missing. So which one of those 2 outputs is the right one? You'll never know, since they were basically in a race condition, running both in parallel under different mutexes (so, identical as not having a mutex at all). If you wanna go fancy you can implement a multi-mutex scheme, one for each some memory area size, and only lock the specific mutex for the area the bloom filter writes. This may increase throughput, or it may not, the right balance needs to be found by trial and error. But this is not a programming thread, after all.  LE: another option is to compute the points in parallel, and queue them in a producer-consumer fashion. And consuming the queue in a single thread, that only does the BF insertions. This simply moves the sync on the queue itself, of course, if you don't want to mess with the bloom class. for (int i = 0; i < POINTS_BATCH_SIZE; i++) { // inserting all batch points into the bloomfilter BloomP.x = pointBatchX[i]; BloomP.y = pointBatchY[i]; std::lock_guard<std::mutex> lock(mtx); bf.insert(secp256k1->GetPublicKeyHex(BloomP)); }
Exactly. Locking the bf like this leads to nothing. The same. May be no diff. Might be multiple. But the running instance yields the right result even so no matter what. Has no impact after all. 69 bits. Tested. The thing was only not putting the mutex for each bloom in relative to them global scope. Updated accordingly. alexander@alexander-home:~/Documents/Test_Dir/Point_Search_GMP$ diff bloom1.bf bloom1B.bf alexander@alexander-home:~/Documents/Test_Dir/Point_Search_GMP$ diff bloom2.bf bloom2B.bf Locking the batch compared to locking just bf.insert is faster time-wise.
|
|
|
|
vneos
Jr. Member
Offline
Activity: 35
Merit: 12
|
 |
June 04, 2025, 01:26:59 PM |
|
Does anyone know who the author of the puzzle is?JPL?…
Adam Back I think it's satoshi himself. The way @satoshi and @saatoshi_rising break up their sentences is the same. Looking at their posts, they both like to leave two spaces after a period. @saatoshi_rising: "I am the creator" is also quite thought-provoking. Does it refer to the creator of the puzzle, or the creator of bitcoin?  Of course, this is just my guess.
|
|
|
|
axwfae
Newbie
Offline
Activity: 1
Merit: 0
|
 |
June 04, 2025, 02:28:49 PM |
|
Who wrote this code?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Share your donation address I can see light at the end of a tunnel.
I wrote it. It's based on the lightweight database for publickeys from @Mcdouglas-X3. The difference is that my script empties RAM and stores the DB in parts compressed on disk. You need a huge amount of storage. And of course the public key. Here is C++ version #include <iostream> #include <atomic> #include <memory> #include <fstream> #include <string> #include <cstdlib> #include <vector> #include <unordered_map> #include <cmath> #include <gmpxx.h> #include <chrono> #include <cassert> #include <cstdio> #include <sys/stat.h> #include <xxhash.h> #include <omp.h> #include <unistd.h> #include <iomanip>
using namespace std;
// Constants and typedefs typedef array<mpz_class, 2> Point;
const mpz_class modulo("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16); const mpz_class order("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); const mpz_class Gx("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16); const mpz_class Gy("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16); const Point PG = {Gx, Gy}; const Point Z = {0, 0};
// Global variables int puzzle = 40; string puzzle_pubkey = "03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4"; int threads = 0; bool verbose = false; const size_t MAX_TABLE_SIZE = 200 * 1024 * 1024; // 200MB per part
// Function declarations void print_help(); bool validate_pubkey(const string& pubkey); Point parse_pubkey(const string& pubkey); inline Point add(const Point& P, const Point& Q, const mpz_class& p = modulo); inline Point mul(const mpz_class& k, const Point& P = PG, const mpz_class& p = modulo); inline Point point_subtraction(const Point& P, const Point& Q); inline mpz_class X2Y(const mpz_class& X, int y_parity, const mpz_class& p = modulo); inline string point_to_cpub(const Point& point); inline string hash_cpub(const string& cpub); void save_baby_table_part(const unordered_map<string, int>& baby_table, int part_num); void save_baby_table(const unordered_map<string, int>& baby_table); unordered_map<string, int> load_baby_table_part(const string& filename); unordered_map<string, int> load_baby_table(); void delete_existing_table(); unordered_map<string, int> generate_baby_table_parallel(const mpz_class& m);
void print_help() { cout << "BSGS (Baby-Step Giant-Step) Elliptic Curve Cryptography Tool\n\n"; cout << "Usage: ./bsgs [options]\n\n"; cout << "Options:\n"; cout << " -p <number> Puzzle number (default: 40)\n"; cout << " -k <pubkey> Compressed public key in hex format\n"; cout << " -t <threads> Number of CPU cores to use (default: all available)\n"; cout << " -v Verbose output\n"; cout << " -h Show this help message\n\n"; cout << "Example:\n"; cout << " ./bsgs -p 40 -k 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4 -t 8\n"; }
bool validate_pubkey(const string& pubkey) { if (pubkey.length() != 66) { cerr << "[error] Public key must be 66 characters long (including 02/03 prefix)\n"; return false; } if (pubkey[0] != '0' || (pubkey[1] != '2' && pubkey[1] != '3')) { cerr << "[error] Public key must start with 02 or 03\n"; return false; } for (size_t i = 2; i < pubkey.length(); i++) { if (!isxdigit(pubkey[i])) { cerr << "[error] Public key contains invalid hex characters\n"; return false; } } return true; }
Point parse_pubkey(const string& pubkey) { string prefix = pubkey.substr(0, 2); mpz_class X(pubkey.substr(2), 16); int y_parity = stoi(prefix) - 2; mpz_class Y = X2Y(X, y_parity); if (Y == 0) { cerr << "[error] Invalid compressed public key!\n"; exit(1); } return {X, Y}; }
inline Point add(const Point& P, const Point& Q, const mpz_class& p) { if (P == Z) return Q; if (Q == Z) return P; const mpz_class& P0 = P[0]; const mpz_class& P1 = P[1]; const mpz_class& Q0 = Q[0]; const mpz_class& Q1 = Q[1]; mpz_class lmbda, num, denom, inv; if (P != Q) { num = Q1 - P1; denom = Q0 - P0; } else { if (P1 == 0) return Z; num = 3 * P0 * P0; denom = 2 * P1; } mpz_invert(inv.get_mpz_t(), denom.get_mpz_t(), p.get_mpz_t()); lmbda = (num * inv) % p; mpz_class x = (lmbda * lmbda - P0 - Q0) % p; if (x < 0) x += p; mpz_class y = (lmbda * (P0 - x) - P1) % p; if (y < 0) y += p; return {x, y}; }
inline Point mul(const mpz_class& k, const Point& P, const mpz_class& p) { Point R0 = Z, R1 = P; unsigned long bit_length = mpz_sizeinbase(k.get_mpz_t(), 2); for (unsigned long i = bit_length - 1; i < bit_length; --i) { if (mpz_tstbit(k.get_mpz_t(), i)) { R0 = add(R0, R1, p); R1 = add(R1, R1, p); } else { R1 = add(R0, R1, p); R0 = add(R0, R0, p); } } return R0; }
inline Point point_subtraction(const Point& P, const Point& Q) { Point Q_neg = {Q[0], (-Q[1]) % modulo}; return add(P, Q_neg); }
inline mpz_class X2Y(const mpz_class& X, int y_parity, const mpz_class& p) { mpz_class X_cubed = (X * X * X) % p; mpz_class tmp = (X_cubed + mpz_class(7)) % p; mpz_class Y; mpz_class exp = (p + mpz_class(1)) / mpz_class(4); mpz_powm(Y.get_mpz_t(), tmp.get_mpz_t(), exp.get_mpz_t(), p.get_mpz_t()); if ((Y % 2) != y_parity) { Y = p - Y; } return Y; }
inline string point_to_cpub(const Point& point) { mpz_class x = point[0], y = point[1]; int y_parity = y.get_ui() & 1; string prefix = y_parity == 0 ? "02" : "03"; char buffer[65]; mpz_get_str(buffer, 16, x.get_mpz_t()); return prefix + string(buffer); }
inline string hash_cpub(const string& cpub) { XXH64_hash_t hash = XXH64(cpub.c_str(), cpub.size(), 0); char buffer[17]; snprintf(buffer, sizeof(buffer), "%016lx", hash); return string(buffer, 8); }
void save_baby_table_part(const unordered_map<string, int>& baby_table, int part_num) { string filename = "baby_table_part_" + to_string(part_num); ofstream outfile(filename, ios::binary); if (!outfile) { cerr << "[error] Failed to open file for writing: " << filename << endl; return; } for (const auto& entry : baby_table) { outfile.write(entry.first.c_str(), 8); int index = entry.second; outfile.write(reinterpret_cast<const char*>(&index), sizeof(index)); } if (verbose) { cout << "[+] Saved baby table part " << part_num << " with " << baby_table.size() << " entries" << endl; } string command = "pigz -9 -b 128 -f " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to compress file: " << filename << endl; return; } pclose(pipe); }
void save_baby_table(const unordered_map<string, int>& baby_table) { size_t current_size = 0; int part_num = 1; unordered_map<string, int> current_part; for (const auto& entry : baby_table) { size_t entry_size = entry.first.size() + sizeof(int); if (current_size + entry_size > MAX_TABLE_SIZE && !current_part.empty()) { save_baby_table_part(current_part, part_num++); current_part.clear(); current_size = 0; } current_part.insert(entry); current_size += entry_size; } if (!current_part.empty()) { save_baby_table_part(current_part, part_num); } }
unordered_map<string, int> load_baby_table_part(const string& filename) { string command = "pigz -d -c " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to decompress file: " << filename << endl; return {}; } unordered_map<string, int> baby_table_part; char key_buffer[9]; int index; while (fread(key_buffer, 8, 1, pipe) == 1) { key_buffer[8] = '\0'; fread(&index, sizeof(index), 1, pipe); baby_table_part[key_buffer] = index; } pclose(pipe); return baby_table_part; }
unordered_map<string, int> load_baby_table() { unordered_map<string, int> baby_table; int part_num = 1; while (true) { string filename = "baby_table_part_" + to_string(part_num) + ".gz"; ifstream test(filename); if (!test.good()) { if (part_num == 1) { cerr << "[error] No baby table parts found!" << endl; return {}; } break; } test.close(); string command = "pigz -d -c " + filename; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cerr << "[error] Failed to decompress file: " << filename << endl; return {}; } char key[9] = {0}; int index; size_t entries = 0; while (fread(key, 8, 1, pipe) == 1) { if (fread(&index, sizeof(int), 1, pipe) != 1) break; key[8] = '\0'; baby_table[key] = index; entries++; } pclose(pipe); if (verbose) { cout << "[+] Loaded part " << part_num << " with " << entries << " entries" << endl; } part_num++; } cout << "[+] Loaded baby table with " << baby_table.size() << " total entries" << endl; return baby_table; }
void delete_existing_table() { int part_num = 1; while (true) { string filename = "baby_table_part_" + to_string(part_num); string compressed_filename = filename + ".gz"; struct stat buffer; bool found = false; if (stat(filename.c_str(), &buffer) == 0) { if (remove(filename.c_str()) != 0) { cerr << "[error] Failed to delete file: " << filename << endl; } else { found = true; } } if (stat(compressed_filename.c_str(), &buffer) == 0) { if (remove(compressed_filename.c_str()) != 0) { cerr << "[error] Failed to delete file: " << compressed_filename << endl; } else { found = true; } } if (!found) { if (part_num == 1 && verbose) { cout << "[+] No existing table parts found to delete" << endl; } break; } part_num++; } }
unordered_map<string, int> generate_baby_table_parallel(const mpz_class& m) { const int num_threads = threads > 0 ? threads : omp_get_max_threads(); const unsigned long total_entries = m.get_ui(); const size_t max_part_size = MAX_TABLE_SIZE * 0.99; // 99% threshold std::atomic<int> parts_created(0); std::atomic<size_t> current_part_size(0); std::atomic<size_t> total_entries_written(0); system("rm -f baby_table_part_* baby_table_part_*.gz 2>/dev/null"); cout << "[+] Generating " << total_entries << " baby steps" << endl;
#pragma omp parallel num_threads(num_threads) { vector<pair<string, int>> local_buffer; local_buffer.reserve(100000);
#pragma omp for schedule(dynamic, 100000) for (unsigned long i = 0; i < total_entries; ++i) { Point P = mul(i); string cpub_hash = hash_cpub(point_to_cpub(P)); local_buffer.emplace_back(cpub_hash, i);
if (local_buffer.size() >= 100000) { #pragma omp critical { int current_part = parts_created + 1; string filename = "baby_table_part_" + to_string(current_part); ofstream outfile(filename, ios::binary | ios::app); if (outfile) { for (const auto& entry : local_buffer) { outfile.write(entry.first.c_str(), 8); outfile.write(reinterpret_cast<const char*>(&entry.second), sizeof(int)); total_entries_written++; current_part_size += 12; if (current_part_size >= max_part_size) { outfile.close(); string cmd = "pigz -9 -b 128 -f " + filename; system(cmd.c_str()); parts_created++; current_part_size = 0; } } } local_buffer.clear(); } } }
#pragma omp critical { if (!local_buffer.empty()) { int current_part = parts_created + 1; string filename = "baby_table_part_" + to_string(current_part); ofstream outfile(filename, ios::binary | ios::app); if (outfile) { for (const auto& entry : local_buffer) { outfile.write(entry.first.c_str(), 8); outfile.write(reinterpret_cast<const char*>(&entry.second), sizeof(int)); total_entries_written++; } outfile.close(); string cmd = "pigz -9 -b 128 -f " + filename; system(cmd.c_str()); parts_created++; } } } }
cout << "[+] Generated " << parts_created << " compressed parts (" << total_entries_written << " total entries) " << endl;
return {}; }
int main(int argc, char* argv[]) { // Parse command line arguments int opt; while ((opt = getopt(argc, argv, "p:k:t:vh")) != -1) { switch (opt) { case 'p': puzzle = atoi(optarg); if (puzzle < 1 || puzzle > 256) { cerr << "[error] Invalid puzzle number (must be between 1-256)\n"; print_help(); return 1; } break; case 'k': puzzle_pubkey = optarg; if (!validate_pubkey(puzzle_pubkey)) { print_help(); return 1; } break; case 't': threads = atoi(optarg); if (threads < 1) { cerr << "[error] Thread count must be at least 1\n"; print_help(); return 1; } break; case 'v': verbose = true; break; case 'h': default: print_help(); return 0; } }
// Initialize OpenMP threads if (threads > 0) { omp_set_num_threads(threads); } int actual_threads = omp_get_max_threads();
// Print configuration time_t currentTime = time(nullptr); cout << "\n\033[01;33m[+]\033[32m BSGS Started: \033[01;33m" << ctime(¤tTime); cout << "\033[0m[+] Puzzle: " << puzzle << endl; cout << "[+] Public Key: " << puzzle_pubkey << endl; cout << "[+] Using " << actual_threads << " CPU cores" << endl;
// Initialize range and point mpz_class start_range = mpz_class(1) << (puzzle - 1); mpz_class end_range = (mpz_class(1) << puzzle) - 1; Point P = parse_pubkey(puzzle_pubkey);
// Calculate m and generate baby table mpz_class m = sqrt(end_range - start_range); m = m * 4; Point m_P = mul(m);
if (verbose) { cout << "[+] Range: 2^" << (puzzle-1) << " to 2^" << puzzle << "-1" << endl; cout << "[+] Baby-step count (m): " << m << endl; }
delete_existing_table();
cout << "[+] Generating baby table..." << endl; auto baby_table = generate_baby_table_parallel(m);
cout << "[+] Loading baby table..." << endl; baby_table = load_baby_table(); if (baby_table.empty()) { cerr << "[error] Failed to load baby table\n"; return 1; }
cout << "[+] Starting BSGS search..." << endl; Point S = point_subtraction(P, mul(start_range)); mpz_class step = 0; bool found = false; mpz_class found_key; auto st = chrono::high_resolution_clock::now(); #pragma omp parallel { Point local_S = S; mpz_class local_step = step; #pragma omp for schedule(dynamic) for (int i = 0; i < actual_threads; ++i) { while (local_step < (end_range - start_range)) { #pragma omp flush(found) if (found) break; string cpub = point_to_cpub(local_S); string cpub_hash = hash_cpub(cpub); auto it = baby_table.find(cpub_hash); if (it != baby_table.end()) { int b = it->second; mpz_class k = start_range + local_step + b; if (point_to_cpub(mul(k)) == puzzle_pubkey) { #pragma omp critical { if (!found) { found = true; found_key = k; auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;32m[+] Solution found!\033[0m" << endl; cout << "[+] Private key: " << k << endl; cout << "[+] Hex: 0x" << hex << k << dec << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; } } #pragma omp flush(found) break; } } local_S = point_subtraction(local_S, m_P); local_step += m; } } }
if (!found) { auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;31m[!] Key not found in the specified range\033[0m" << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; }
return 0; } Makefile # Detect OS UNAME_S := $(shell uname -s)
# Enable static linking by default (change to 'no' to use dynamic linking) STATIC_LINKING = yes
# Compiler settings based on OS ifeq ($(UNAME_S),Linux) # Linux settings
# Compiler CXX = g++
# Compiler flags CXXFLAGS = -m64 -std=c++17 -march=native -mtune=native -Ofast -mssse3 -Wall -Wextra \ -funroll-loops -ftree-vectorize -fstrict-aliasing -fno-semantic-interposition \ -fvect-cost-model=unlimited -fno-trapping-math -fipa-ra -flto -fopenmp \ -mavx2 -mbmi2 -madx LDFLAGS = -lgmp -lgmpxx -lxxhash
# Source files SRCS = bsgs.cpp
# Object files OBJS = $(SRCS:.cpp=.o)
# Target executable TARGET = bsgs
# Default target all: $(TARGET)
# Link the object files to create the executable $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) 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: all clean
else # Windows settings (MinGW-w64)
# Compiler CXX = g++
# Check if compiler is found CHECK_COMPILER := $(shell which $(CXX) 2>/dev/null)
# Add MSYS path if the compiler is not found ifeq ($(CHECK_COMPILER),) $(info Compiler not found. Adding MSYS path to the environment...) SHELL := cmd PATH := C:\msys64\mingw64\bin;$(PATH) endif
# Compiler flags CXXFLAGS = -m64 -std=c++17 -march=native -mtune=native -Ofast -mssse3 -Wall -Wextra \ -funroll-loops -ftree-vectorize -fstrict-aliasing -fno-semantic-interposition \ -fvect-cost-model=unlimited -fno-trapping-math -fipa-ra -flto -fopenmp \ -mavx2 -mbmi2 -madx LDFLAGS = -lgmp -lgmpxx -lxxhash
# Add -static flag if STATIC_LINKING is enabled ifeq ($(STATIC_LINKING),yes) LDFLAGS += -static else $(info Dynamic linking will be used. Ensure required DLLs are distributed) endif
# Source files SRCS = bsgs.cpp
# Object files OBJS = $(SRCS:.cpp=.o)
# Target executable TARGET = bsgs.exe
# Default target all: $(TARGET)
# Link the object files to create the executable $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) del /q $(OBJS) 2>nul
# Compile each source file into an object file %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@
# Clean up build files clean: @echo Cleaning... del /q $(OBJS) $(TARGET) 2>nul
.PHONY: all clean endif dependencies: sudo apt install libgmp-dev libomp-dev xxhash pigz - BSGS Started: Wed Jun 4 01:56:48 2025
- Puzzle: 40
- Public Key: 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4
- Using 12 CPU cores
- Generating baby table...
- Generating 2965820 baby steps
- Generated 1 compressed parts (2965820 total entries)
- Loading baby table...
- Loaded baby table with 2964816 total entries
- Starting BSGS search...
- Solution found!
- Private key: 1003651412950
- Hex: 0xe9ae4933d6
- Time elapsed: 0.612888 seconds
# ./bsgs -p 50 -k 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6 -t 12 - BSGS Started: Wed Jun 4 02:20:52 2025
- Puzzle: 50
- Public Key: 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6
- Using 12 CPU cores
- Generating baby table...
- Generating 94906264 baby steps
- Generated 6 compressed parts (94906264 total entries)
- Loading baby table...
- Loaded baby table with 93398236 total entries
- Starting BSGS search...
- Solution found!
- Private key: 611140496167764
- Hex: 0x22bd43c2e9354
- Time elapsed: 4.83157 seconds
P.S. This can be easily modified to use a Bloom Filter. And probably the GPU version. Use it for whatever you want. make fail , 1. sudo apt install libxxhash-dev 2. bsgs.cpp add " #include <array>" I'm very sorry, my English is not good, I can only explain it this way
|
|
|
|
Akito S. M. Hosana
Jr. Member
Offline
Activity: 336
Merit: 8
|
 |
June 04, 2025, 04:31:03 PM |
|
make fail ,
1. sudo apt install libxxhash-dev 2. bsgs.cpp add " #include <array>"
I'm very sorry, my English is not good, I can only explain it this way
Yep. On Windows it must be like this. But he doesn't have Windows to see. 
|
|
|
|
nomachine
|
 |
June 04, 2025, 04:57:45 PM |
|
I think it's satoshi himself. The way @satoshi and @saatoshi_rising break up their sentences is the same. Looking at their posts, they both like to leave two spaces after a period. @saatoshi_rising: "I am the creator" is also quite thought-provoking. Does it refer to the creator of the puzzle, or the creator of bitcoin?  Of course, this is just my guess. Satoshi rarely used phrases like "by way of excuse" or "what is especially embarrassing." or "I will make up for two years of stupidity". There is no cryptographic signature or undeniable link to Satoshi unless proven otherwise. Probably not Satoshi. But who knows? He could be anyone (a pseudonym) here… or no one at all. 
|
BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
|
|
|
kTimesG
|
Locking the batch compared to locking just bf.insert is faster time-wise.
This probably depends on the batch size. Since the atomic lock is basically just a clock cycle, this might indicate that multiple cores are often accessing overlapping RAM areas, forcing I/O between CPU caches and main memory. I think a synchronized queue fits better, and it fills the compute pipeline. Add the hashed pubKeys to the queue (and signal addition), consume them from an auxiliary thread (using signaled events). This ensures no false sharing of L1 cache occurs, and no threads get blocked (unless the queue itself goes OOM). 
|
Off the grid, training pigeons to broadcast signed messages.
|
|
|
nomachine
|
 |
June 04, 2025, 07:00:02 PM |
|
This probably depends on the batch size. Since the atomic lock is basically just a clock cycle, this might indicate that multiple cores are often accessing overlapping RAM areas, forcing I/O between CPU caches and main memory.
I also have a problem with my implementation. The current implementation loads the entire baby table into memory before starting the search phase. For large puzzles (like puzzle 70), this becomes impractical. For puzzle 70, the baby table would contain approximately 2^35 entries (34 billion+). With each entry being ~12 bytes (8-byte hash + 4-byte index), this would require ~512GB of RAM. Instead of loading the entire table at once: Process each compressed partition one at a time. For each DB part: Decompress it to memory...Search against the current DB part....Discard it before loading the next DB part // Modified search function void partitioned_search(const Point& S, const mpz_class& start_range, const mpz_class& end_range, const Point& m_P, const string& puzzle_pubkey) { bool found = false; mpz_class found_key; int part_num = 1; auto st = chrono::high_resolution_clock::now();
while (!found) { string filename = "baby_table_part_" + to_string(part_num) + ".gz"; ifstream test(filename); if (!test.good()) { if (part_num == 1) { cerr << "[error] No baby table parts found!" << endl; return; } break; // No more parts } test.close();
// Load just this part auto baby_table_part = load_baby_table_part(filename); if (baby_table_part.empty()) { part_num++; continue; }
if (verbose) { cout << "[+] Searching part " << part_num << " with " << baby_table_part.size() << " entries" << endl; }
#pragma omp parallel { Point local_S = S; mpz_class local_step = 0; #pragma omp for schedule(dynamic) for (int i = 0; i < omp_get_num_threads(); ++i) { while (local_step < (end_range - start_range)) { if (found) break; string cpub = point_to_cpub(local_S); string cpub_hash = hash_cpub(cpub); auto it = baby_table_part.find(cpub_hash); if (it != baby_table_part.end()) { int b = it->second; mpz_class k = start_range + local_step + b; if (point_to_cpub(mul(k)) == puzzle_pubkey) { #pragma omp critical { if (!found) { found = true; found_key = k; auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;32m[+] Solution found!\033[0m" << endl; cout << "[+] Private key: " << k << endl; cout << "[+] Hex: 0x" << hex << k << dec << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; } } break; } } local_S = point_subtraction(local_S, m_P); local_step += m; } } } part_num++; }
if (!found) { auto et = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = et - st; cout << "\n\033[01;31m[!] Key not found in the specified range\033[0m" << endl; cout << "[+] Time elapsed: " << elapsed.count() << " seconds\n"; } }
|
BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
|
|
|
AlexanderCurl
Jr. Member
Offline
Activity: 33
Merit: 171
|
 |
June 04, 2025, 07:00:23 PM |
|
Locking the batch compared to locking just bf.insert is faster time-wise.
This probably depends on the batch size. Since the atomic lock is basically just a clock cycle, this might indicate that multiple cores are often accessing overlapping RAM areas, forcing I/O between CPU caches and main memory. I think a synchronized queue fits better, and it fills the compute pipeline. Add the hashed pubKeys to the queue (and signal addition), consume them from an auxiliary thread (using signaled events). This ensures no false sharing of L1 cache occurs, and no threads get blocked (unless the queue itself goes OOM).  Thanks a lot for explanations. Will definitely see to it. But I do not feel like improving Point_Search further. At least not now. I have recently came to a new idea. Will devote my spare time to put it to code.
|
|
|
|
FrozenThroneGuy
Jr. Member
Offline
Activity: 44
Merit: 20
|
 |
June 04, 2025, 07:02:53 PM Last edit: June 04, 2025, 08:05:38 PM by FrozenThroneGuy |
|
3 seconds on PYTHON! PK found.
import math, time, sys, os from gmpy2 import mpz, powmod, invert, jacobi import xxhash from sortedcontainers import SortedDict
# Clear screen and initialize os.system("cls||clear") t = time.ctime() sys.stdout.write(f"\033[?25l\033[01;33m[+] BSGS: {t}\n") sys.stdout.flush()
# Elliptic Curve Parameters (secp256k1) modulo = mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) order = mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141) Gx = mpz(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) Gy = mpz(0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) PG = (Gx, Gy)
# Point Addition on Elliptic Curve def add(P, Q): if P == (0, 0): return Q if Q == (0, 0): return P Px, Py = P Qx, Qy = Q if Px == Qx: if Py == Qy: inv_2Py = invert((Py << 1) % modulo, modulo) m = (3 * Px * Px * inv_2Py) % modulo else: return (0, 0) else: inv_diff_x = invert(Qx - Px, modulo) m = ((Qy - Py) * inv_diff_x) % modulo x = (m * m - Px - Qx) % modulo y = (m * (Px - x) - Py) % modulo return (x, y)
# Scalar Multiplication on Elliptic Curve def mul(k, P=PG): R0, R1 = (0, 0), P for i in reversed(range(k.bit_length())): if (k >> i) & 1: R0, R1 = add(R0, R1), add(R1, R1) else: R1, R0 = add(R0, R1), add(R0, R0) return R0
# Point Subtraction def point_subtraction(P, Q): Q_neg = (Q[0], (-Q[1]) % modulo) return add(P, Q_neg)
# Compute Y from X using curve equation def X2Y(X, y_parity, p=modulo): X3_7 = (pow(X, 3, p) + 7) % p if jacobi(X3_7, p) != 1: return None Y = powmod(X3_7, (p + 1) >> 2, p) return Y if (Y & 1) == y_parity else (p - Y)
# Convert point to compressed public key def point_to_cpub(point): x, y = point y_parity = y & 1 prefix = '02' if y_parity == 0 else '03' compressed_pubkey = prefix + format(x, '064x') return compressed_pubkey
# Hash a compressed public key using xxhash and store only the first 8 characters def hash_cpub(cpub): return xxhash.xxh64(cpub.encode()).hexdigest()[:8]
# Main Script if __name__ == "__main__": # Puzzle Parameters puzzle = 40 start_range, end_range = 2**(puzzle-1), (2**puzzle) - 1 puzzle_pubkey = '03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4'
# Parse Public Key if len(puzzle_pubkey) != 66: print("[error] Public key length invalid!") sys.exit(1) prefix = puzzle_pubkey[:2] X = mpz(int(puzzle_pubkey[2:], 16)) y_parity = int(prefix) - 2 Y = X2Y(X, y_parity) if Y is None: print("[error] Invalid compressed public key!") sys.exit(1) P = (X, Y) # Uncompressed public key
# Precompute m and mP for BSGS m = int(math.floor(math.sqrt(end_range - start_range))) m_P = mul(m)
# Create Baby Table with SortedDict print('[+] Creating babyTable...') baby_table = SortedDict() Ps = (0, 0) # Start with the point at infinity for i in range(m + 1): cpub = point_to_cpub(Ps) cpub_hash = hash_cpub(cpub) # Use xxhash and store only 8 characters baby_table[cpub_hash] = i # Store the hash as the key and index as the value Ps = add(Ps, PG) # Incrementally add PG
# BSGS Search print('[+] BSGS Search in progress') S = point_subtraction(P, mul(start_range)) step = 0 st = time.time() while step < (end_range - start_range): cpub = point_to_cpub(S) cpub_hash = hash_cpub(cpub) # Hash the current compressed public key # Check if the hash exists in the baby_table if cpub_hash in baby_table: b = baby_table[cpub_hash] k = start_range + step + b if point_to_cpub(mul(k)) == puzzle_pubkey: print(f'[+] m={m} step={step} b={b}') print(f'[+] Key found: {k}') print("[+] Time Spent : {0:.2f} seconds".format(time.time() - st)) sys.exit() S = point_subtraction(S, m_P) step += m
print('[+] Key not found') print("[+] Time Spent : {0:.2f} seconds".format(time.time() - st)) puzzle 40 - BSGS: Thu Feb 20 21:49:30 2025
- Creating babyTable...
- BSGS Search in progress
- m=741455 step=453895024440 b=574622
- Key found: 1003651412950
- Time Spent : 2.90 seconds
puzzle 50 - BSGS: Thu Feb 20 22:13:12 2025
- Creating babyTable...
- BSGS Search in progress
- m=23726566 step=48190529944714 b=12801738
- Key found: 611140496167764
- Time Spent : 12.71 seconds
This is the result... on a single core  P.S. For puzzles above 50, you'll need a Bloom Filter Who wrote this code?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Share your donation address I can see light at the end of a tunnel. Yes, I agree that NoMachine is great for coding, but did you know it only works if you have a public key!!? That’s clearly tied to the current puzzles. Also, there's a better option from RC that uses GPU. Regards. Puzzle 40 and 50 root@DESKTOP-BD9V01U:/mnt/e/Kang# ./Mark1 --range 562949953421311:1125899906842623 --pubkey 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6 --dp_point 1000000 --dp_bits 8
=========== Phase-0: RAM summary =========== DP table : 52.1Mb ( 1000000 / 1333334 slots, load 75.00% ) Bloom : 1.91Mb -------------------------------------------- Total : 54.0Mb
========== Phase-1: Building traps ============ Unique traps: 1000000/1000000 (done)
=========== Phase-2: Kangaroos ============= Speed: 2.52 MH/s | Hops: 12582912 | Time: 0:0:5
============= Phase-3: Result ============== Private key : 0x00000000000000000000000000000000000000000000000000022BD43C2E9354 Found by thread: 3 Total time: 00:00:00.217
Private key saved to FOUND.txt root@DESKTOP-BD9V01U:/mnt/e/Kang# ./Mark1 --range 549755813887:1199511627775 --pubkey 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4 --dp_point 10000 --dp_bits 8
=========== Phase-0: RAM summary =========== DP table : 534Kb ( 10000 / 13334 slots, load 75.00% ) Bloom : 19.5Kb -------------------------------------------- Total : 553Kb
========== Phase-1: Building traps ============ Unique traps: 10000/10000 (done)
=========== Phase-2: Kangaroos ============= Speed: 0.00 H/s | Hops: 0 | Time: 0:0:5
============= Phase-3: Result ============== Private key : 0x000000000000000000000000000000000000000000000000000000E9AE4933D6 Found by thread: 4 Total time: 00:00:00.037
Private key saved to FOUND.txt
Play with kangs... Puzzle 55 root@DESKTOP-BD9V01U:/mnt/e/Triarch/Kang# ./Mark1 --range 18014398509481983:36028797018963967 --pubkey 0385a30d8413af4f8f9e6312400f2d194fe14f02e719b24c3f83bf1fd233a8f963 --dp_bits 12 --dp_point 1000000
=========== Phase-0: RAM summary =========== DP table : 52.1Mb ( 1000000 / 1333334 slots, load 75.00% ) Bloom : 1.91Mb -------------------------------------------- Total : 54.0Mb
========== Phase-1: Building traps ========= Unique traps: 1000000/1000000 (done)
=========== Phase-2: Kangaroos ============= Speed: 15.94 MH/s | Hops: 79691776 | Time: 0:0:5
============= Phase-3: Result ============== Private key : 0x000000000000000000000000000000000000000000000000006ABE1F9B67E114 Found by thread: 10 Total time: 00:00:01.251 Private key saved to FOUND.txt
Puzzle 60 root@DESKTOP-BD9V01U:/mnt/e/Triarch/Kang# ./Mark1 --range 576460752303423487:1152921504606846975 --pubkey 0348e843dc5b1bd246e6309b4924b81543d02b16c8083df973a89ce2c7eb89a10d --dp_bits 10 --dp_point 10000000
=========== Phase-0: RAM summary =========== DP table : 521Mb ( 10000000 / 13333334 slots, load 75.00% ) Bloom : 19.1Mb -------------------------------------------- Total : 540Mb
========== Phase-1: Building traps ========= Unique traps: 10000000/10000000 (done)
=========== Phase-2: Kangaroos ============= Speed: 44.30 MH/s | Hops: 548667392 | Time: 0:0:10
============= Phase-3: Result ============== Private key : 0x0000000000000000000000000000000000000000000000000FC07A1825367BBE Found by thread: 2 Total time: 00:00:08.347 Private key saved to FOUND.txt
Puzzle 70 root@DESKTOP-BD9V01U:/mnt/e/Triarch/Kang# ./Mark1 --range 590295810358705651711:1180591620717411303423 --pubkey 0290e6900a58d33393bc1097b5aed31f2e4e7cbd3e5466af958665bc0121248483 --dp_point 100000000 --dp_bits 10
=========== Phase-0: RAM summary =========== DP table : 5.09Gb ( 100000000 / 133333334 slots, load 75.00% ) Bloom : 191Mb -------------------------------------------- Total : 5.28Gb
========== Phase-1: Building traps ========= Unique traps: 100000000/100000000 (done)
=========== Phase-2: Kangaroos ============= Speed: 20.45 MH/s | Hops: 14508097536 | Time: 0:3:45
============= Phase-3: Result ============== Private key : 0x0000000000000000000000000000000000000000000000349B84B6431A6C4EF1 Found by thread: 1 Total time: 00:03:41.587 Private key saved to FOUND.txt
And this is not so powerful CPU Ryzen 7 5800H
|
|
|
|
Akito S. M. Hosana
Jr. Member
Offline
Activity: 336
Merit: 8
|
 |
June 04, 2025, 07:44:14 PM |
|
~~ snippet ~~
I wonder how many million years it takes you to solve puzzle 135? 
|
|
|
|
FrozenThroneGuy
Jr. Member
Offline
Activity: 44
Merit: 20
|
 |
June 04, 2025, 07:52:27 PM |
|
~~ snippet ~~
I wonder how many million years it takes you to solve puzzle 135?  On 1 cpu with my algo 2.288 years. 1 gpu like rtx4060 - 288 years, 10 gpu - 28 years. 10 rtx4090 - 6 may be 7 years.
|
|
|
|
Akito S. M. Hosana
Jr. Member
Offline
Activity: 336
Merit: 8
|
 |
June 04, 2025, 07:55:27 PM |
|
Everyone give up, don't be cheated of life, because time is life.
You repeat the same sentence like a parrot. 
|
|
|
|
IlhamCung23
Newbie
Offline
Activity: 8
Merit: 0
|
 |
June 04, 2025, 09:44:25 PM |
|
candidate for 71 puzzle prefix 6 digits  118393 119305 119315 119324 119334 119344 119354 119363 119373 119383 119392 120305 120314 120324 120334 120343 120353 120363 120373 120382 120392 121304 121314 121324 121333 121343 121353 121362 121372 121382 121391 122207 122216 122226 122236 122245 122255 122265 122275 122284 122294 123206 123216 123226 123235 123245 123255 123264 123274 123284 123294 124206 124215 124225 124235 124245 124254 124264 124274 124283 124293 125108 125118 125128 125137 125147 125157 125166 125176 125186 125196 126108 126117 126127 126137 126147 126156 126166 126176 126185 126195 127107 127117 127127 127136 127146 127156 127166 127175 127185 127195 128000 128010 128020 128029 128039 128049 128058 128068 128078 128087 128097 129000 129009 129019 129029 129039 129048 129058 129068 129077 129087 129097 130009 130019 130028 130038 130048 130057 130067 130077 130087 130096 131008 131018 131028 131038 131047 131057 131067 131076 131086 131096 131901 131911 131921 131930 131940 131950 131960 131969 131979 131989 131998 132901 132911 132920 132930 132940 132949 132959 132969 132978 132988 132998 133900 133910 133920 133930 133939 133949 133959 133968 133978 133988 133997 134803 134813 134822 134832 134842 134851 134861 134871 134881 134890 135802 135812 135822 135832 135841 135851 135861 135870 135880 135890 135899 136802 136812 136821 136831 136841 136851 136860 136870 136880 136889 136899 137802 137811 137821 137831 137840 137850 137860 137869 137879 137889 137899 138704 138714 138723 138733 138743 138753 138762 138772 138782 138791 139704 139713 139723 139733 139742 139752 139762 139772 139781 139791 140703 140713 140723 140732 140742 140752 140761 140771 140781 140790 141606 141615 141625 141635 141644 141654 141664 141674 141683 141693 142605 142615 142625 142634 142644 142654 142663 142673 142683 142693 143605 143614 143624 143634 143644 143653 143663 143673 143682 143692 144507 144517 144527 144536 144546 144556 144565 144575 144585 144595 145507 145517 145526 145536 145546 145555 145565 145575 145584 145594 146506 146516 146526 146535 146545 146555 146565 146574 146584 146594 147506 147516 147525 147535 147545 147554 147564 147574 147584 147593 148408 148418 148428 148438 148447 148457 148467 148476 148486 148496 149408 149418 149427 149437 149447 149456 149466 149476 149486 149495 150408 150417 150427 150437 150446 150456 150466 150475 150485 150495 151300 151310 151320 151329 151339 151349 151359 151368 151378 151388 151397 152300 152310 152319 152329 152339 152348 152358 152368 152378 152387 152397 153309 153319 153329 153338 153348 153358 153367 153377 153387 153396 154202 154212 154221 154231 154241 154250 154260 154270 154280 154289 154299 155201 155211 155221 155231 155240 155250 155260 155269 155279 155289 155299 156201 156211 156220 156230 156240 156250 156259 156269 156279 156288 156298 157201 157210 157220 157230 157239 157249 157259 157269 157278 157288 157298 158103 158113 158123 158132 158142 158152 158161 158171 158181 158190 159103 159112 159122 159132 159141 159151 159161 159171 159180 159190 160102 160112 160122 160131 160141 160151 160160 160170 160180 160190 160199 161005 161014 161024 161034 161044 161053 161063 161073 161082 161092 162004 162014 162024 162033 162043 162053 162062 162072 162082 162092 163004 163014 163023 163033 163043 163052 163062 163072 163081 163091 164003 164013 164023 164032 164042 164052 164062 164071 164081 164091 164906 164916 164925 164935 164945 164954 164964 164974 164983 164993 165905 165915 165925 165935 165944 165954 165964 165973 165983 165993 166905 166915 166924 166934 166944 166953 166963 166973 166983 166992 167807 167817 167827 167837 167846 167856 167866 167875 167885 167895 168807 168817 168826 168836 168846 168856 168865 168875 168885 168894 169807 169816 169826 169836 169845 169855 169865 169874 169884 169894 170709 170719 170728 170738 170748 170758 170767 170777 170787 170796 171709 171718 171728 171738 171747 171757 171767 171777 171786 171796 172708 172718 172728 172737 172747 172757 172766 172776 172786 172796 173708 173717 173727 173737 173747 173756 173766 173776 173785 173795 174601 174610 174620 174630 174639 174649 174659 174668 174678 174688 174698 175600 175610 175619 175629 175639 175649 175658 175668 175678 175687 175697 176600 176609 176619 176629 176638 176648 176658 176668 176677 176687 176697 177502 177512 177522 177531 177541 177551 177560 177570 177580 177589 177599 178502 178511 178521 178531 178540 178550 178560 178570 178579 178589 178599 179501 179511 179521 179530 179540 179550 179559 179569 179579 179589 179598 180501 180510 180520 180530 180540 180549 180559 180569 180578 180588 180598 181403 181413 181423 181432 181442 181452 181462 181471 181481 181491 182403 182413 182422 182432 182442 182451 182461 182471 182480 182490 183402 183412 183422 183431 183441 183451 183461 183470 183480 183490 183499 184305 184315 184324 184334 184344 184353 184363 184373 184383 184392 185304 185314 185324 185334 185343 185353 185363 185372 185382 185392 186304 186314 186323 186333 186343 186353 186362 186372 186382 186391 187206 187216 187226 187236 187245 187255 187265 187274 187284 187294 188206 188216 188225 188235 188245 188255 188264 188274 188284 188293 189206 189215 189225 189235 189244 189254 189264 189274 189283 189293 190205 190215 190225 190234 190244 190254 190263 190273 190283 190292 191108 191117 191127 191137 191146 191156 191166 191176 191185 191195 192107 192117 192127 192136 192146 192156 192165 192175 192185 192195 193107 193116 193126 193136 193146 193155 193165 193175 193184 193194 194000 194009 194019 194029 194038 194048 194058 194067 194077 194087 194097 195009 195019 195028 195038 195048 195057 195067 195077 195086 195096 196008 196018 196028 196037 196047 196057 196067 196076 196086 196096 196901 196911 196921 196930 196940 196950 196959 196969 196979 196989 196998 197901 197910 197920 197930 197940 197949 197959 197969 197978 197988 197998 198900 198910 198920 198929 198939 198949 198958 198968 198978 198988 198997 199900 199910 199919 199929 199939 199948 199958 199968 199977 199987 199997 200802 200812 200822 200831 200841 200851 200861 200870 200880 200890 200899 201802 201812 201821 201831 201841 201850 201860 201870 201880 201889 201899 202801 202811 202821 202831 202840 202850 202860 202869 202879 202889 202898 203704 203714 203723 203733 203743 203752 203762 203772 203782 203791 204703 204713 204723 204733 204742 204752 204762 204771 204781 204791 205703 205713 205722 205732 205742 205752 205761 205771 205781 205790 206703 206712 206722 206732 206741 206751 206761 206771 206780 206790 207605 207615 207624 207634 207644 207654 207663 207673 207683 207692 208605 208614 208624 208634 208643 208653 208663 208673 208682 208692 209604 209614 209624 209633 209643 209653 209662 209672 209682 209692 210507 210516 210526 210536 210546 210555 210565 210575 210584 210594 211506 211516 211526 211535 211545 211555 211564 211574 211584 211594 212506 212515 212525 212535 212545 212554 212564 212574 212583 212593 213408 213418 213428 213437 213447 213457 213467 213476 213486 213496 214408 214418 214427 214437 214447 214456 214466 214476 214485 214495 215407 215417 215427 215437 215446 215456 215466 215475 215485 215495 216407 216417 216426 216436 216446 216455 216465 216475 216485 216494 217300 217309 217319 217329 217339 217348 217358 217368 217377 217387 217397 218309 218319 218328 218338 218348 218358 218367 218377 218387 218396 219309 219318 219328 219338 219347 219357 219367 219376 219386 219396 220201 220211 220221 220230 220240 220250 220260 220269 220279 220289 220298 221201 221211 221220 221230 221240 221249 221259 221269 221279 221288 221298 222200 222210 222220 222230 222239 222249 222259 222268 222278 222288 222297 223200 223210 223219 223229 223239 223249 223258 223268 223278 223287 223297 224103 224112 224122 224132 224141 224151 224161 224170 224180 224190 225102 225112 225121 225131 225141 225151 225160 225170 225180 225189 225199 226102 226111 226121 226131 226140 226150 226160 226170 226179 226189 226199 227004 227014 227024 227033 227043 227053 227062 227072 227082 227091 228004 228013 228023 228033 228042 228052 228062 228072 228081 228091 229003 229013 229023 229032 229042 229052 229061 229071 229081 229091 229906 229915 229925 229935 229945 229954 229964 229974 229983 229993 230905 230915 230925 230934 230944 230954 230963 230973 230983 230993 231905 231915 231924 231934 231944 231953 231963 231973 231982 231992 232904 232914 232924 232933 232943 232953 232963 232972 232982 232992 233807 233817 233826 233836 233846 233855 233865 233875 233885 233894 234806 234816 234826 234836 234845 234855 234865 234874 234884 234894 235806 235816
|
|
|
|
Akito S. M. Hosana
Jr. Member
Offline
Activity: 336
Merit: 8
|
 |
June 04, 2025, 10:05:33 PM |
|
~~ snippet ~~
What method did you use to get these numbers? 
|
|
|
|
nomachine
|
 |
June 05, 2025, 09:17:54 AM |
|
I wonder how many million years it takes you to solve puzzle 135?  Scripts based on public keys and x and y coordinates are completely useless here because there are no public keys until puzzle #135. There are only two logical options: either invent a new algorithm that will compute HASH160 1000 times faster, or go for 2000 GPUs. I don’t know which method is worse. In any case, this is a dead end. All we can do is showcase useless scripts. Which is exactly what we’re already doing. 
|
BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
|
|
|
Akito S. M. Hosana
Jr. Member
Offline
Activity: 336
Merit: 8
|
 |
June 05, 2025, 09:32:48 AM |
|
invent a new algorithm that will compute HASH160 1000 times faster
How ? 
|
|
|
|
nomachine
|
 |
June 05, 2025, 09:40:47 AM |
|
invent a new algorithm that will compute HASH160 1000 times faster
How ?  I wanna know how too, bro. 
|
BTC: bc1qdwnxr7s08xwelpjy3cc52rrxg63xsmagv50fa8
|
|
|
bibilgin
Newbie
Offline
Activity: 236
Merit: 0
|
 |
June 05, 2025, 12:02:25 PM |
|
Does anyone know who the author of the puzzle is?JPL?…
I know who the creator is. - He cannot be contacted digitally in any way. (But he can reach you.) - Someone who likes to travel in the mountains, go camping, etc. - One of the first people to introduce Bitcoin in the EUROPE region and in his own country during the early days of Bitcoin. - A person who has some interesting comments about the creator of Bitcoin (SATOSHI) and gives clues about who the Satoshi group is. - The head of the Fund donations who helps people in some regions with the help of 6 associations. - Finally, a person who has more than 28500 BTC detected in the total of his accounts. - Just the Last Hint - A citizen of a country you cannot guess (Europe) ...
|
|
|
|
|