Aggiungi versione GPU (CUDA) del bruteforce
Porta la stessa strategia algoritmica della versione CPU (batch EC in coordinate Jacobiane con Z1=1 + un'unica inversione di campo per batch via trucco di Montgomery) su GPU NVIDIA, dove però GMP non è disponibile: l'aritmetica a 256 bit mod il primo di secp256k1 è scritta a mano (limb a 64 bit + __int128, riduzione veloce sfruttando 2^256 ≡ 2^32+977 mod p), e ogni thread CUDA agisce come una lane di ricerca indipendente con la propria chiave privata iniziale casuale — esattamente come un thread CPU, ma migliaia in parallelo invece di una manciata. La moltiplicazione scalare su device è un semplice double-and-add (nessun wNAF/tabelle precalcolate come in libsecp256k1 lato host), il principale margine di ottimizzazione futura. Il matching usa un Bloom filter + ricerca binaria su un array di target ordinato, entrambi caricati una volta sola in memoria device. Aggiunge i target `make gpu` / `make gpu-info` al Makefile (NVCC_ARCH configurabile, default sm_61/Pascal) e documenta in README che su WSL2 il driver NVIDIA arriva dall'host Windows: dentro WSL serve installare solo il CUDA Toolkit, mai un driver. Verificato: aritmetica di campo, moltiplicazione scalare e batch add confrontati bit-per-bit con secp256k1_ec_pubkey_create (nessuna discrepanza su ~30 valori di test); run end-to-end con privkey nota trova la chiave corretta; nessun falso positivo su chiavi pubbliche valide casuali. Su una Quadro P4000 (Pascal): ~15M keys/sec, contro ~3.5-4M keys/sec della CPU con 11 thread nello stesso ambiente.
This commit is contained in:
@@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
Educational/research suite for studying Bitcoin P2PK (Pay-to-Public-Key) transactions and demonstrating why ECDSA secp256k1 bruteforce is computationally infeasible (keyspace 2^256). All docs and CLI output are in Italian. Two independent components share data via files, not code:
|
||||
|
||||
1. `databases/` — Python scanner that walks the Bitcoin blockchain via the mempool.space API, finds P2PK outputs, stores them in SQLite, and checks UTXO spent/unspent status.
|
||||
2. `bruteforce/` — C++ program that loads target public keys and searches the private-key space for matches, as a performance demonstration (not a realistic attack — success probability is ~2^-256).
|
||||
2. `bruteforce/` — CPU (C++/pthreads) and GPU (CUDA) programs that load target public keys and search the private-key space for matches, as a performance demonstration (not a realistic attack — success probability is ~2^-256).
|
||||
|
||||
Data flows one way: scanner → SQLite DB (`databases/bitcoin_p2pk_study.db`) → `extract_p2pk_utxo.py` filters unspent P2PK → `target_keys.txt` → C++ bruteforce consumes it.
|
||||
|
||||
@@ -41,18 +41,32 @@ python3 extract_p2pk_utxo.py [db_path] [output.txt] # default: ../databases/bi
|
||||
python3 extract_p2pk_utxo.py --stats # DB stats only, no extraction
|
||||
|
||||
./p2pk_bruteforce [target_keys.txt] # runs until Ctrl+C; logs to progress.csv, matches to found_keys.txt
|
||||
|
||||
# GPU version (CUDA), much faster if an NVIDIA GPU + driver + CUDA Toolkit are available
|
||||
make gpu-info # checks nvidia-smi and nvcc are present
|
||||
make gpu # NVCC_ARCH defaults to sm_61 (Pascal) — override for other GPUs, e.g. make gpu NVCC_ARCH=sm_86
|
||||
./p2pk_bruteforce_gpu [target_keys.txt]
|
||||
```
|
||||
|
||||
There is no test suite in this repo.
|
||||
|
||||
## Architecture notes
|
||||
|
||||
### `bruteforce/p2pk_bruteforce.cpp` (single file, everything lives here)
|
||||
### `bruteforce/p2pk_bruteforce.cpp` (CPU, single file, everything lives here)
|
||||
- One pthread per (core count − 1); each thread gets a disjoint slice of the 256-bit keyspace via `partition_keyspace` (only the top 64 bits are partitioned — the search relies on random start offsets within each thread's slice, not a full 256-bit range split).
|
||||
- Target pubkeys are loaded from a `.txt` file (uncompressed hex, `04` prefix, one per line, header line skipped) into both an `unordered_map<secp256k1_pubkey,...>` (exact match) and a 64MB Bloom filter (fast negative rejection, checked first via `check_match_fast`).
|
||||
- Performance trick: instead of computing `privkey * G` per candidate, it computes one EC multiplication then derives the next `EC_BATCH_SIZE` (256) keys via EC point addition against precomputed multiples of G (`precompute_generator_multiples`), which is much cheaper than repeated scalar multiplication.
|
||||
- Target pubkeys are loaded from a `.txt` file (uncompressed hex, `04` prefix, one per line, header line skipped) into both an `unordered_map<std::array<uint8_t,64>,...>` keyed on raw X||Y bytes (exact match) and a 64MB Bloom filter (fast negative rejection, checked first via `check_match_fast_raw`).
|
||||
- Performance trick: one EC scalar multiplication (via libsecp256k1) produces a base point P0, then the next `EC_BATCH_SIZE` (256) keys are derived via Jacobian-coordinate EC point addition (`ec_add_affine_affine`, formulas specialized for Z1=1) against precomputed multiples of G, converted back to affine with a single shared modular inversion per batch (Montgomery's batch-inversion trick, via GMP) instead of one inversion per key.
|
||||
- `increment_privkey`/`add_to_privkey` treat the 32-byte scalar as 4 native 64-bit words (little-endian on x86), not as one big-endian integer — this still enumerates the keyspace without collisions, just in a "scrambled" order; don't reuse them to reconstruct an exact scalar from EC math (use `add_small_be256` for that, as `save_found_key` does).
|
||||
- Build system auto-detects a local `bruteforce/secp256k1/` (built by `build_secp256k1.sh` targeting this specific CPU with `-march=native`) and links against it via rpath instead of the system lib; falls back to system `libsecp256k1`/`libgmp` if absent.
|
||||
- Matches are written to `found_keys.txt`; periodic throughput stats go to stdout and `progress.csv` every 10s.
|
||||
- Matches are written to `found_keys.txt`; throughput stats (instantaneous rate, summed across threads) print to stdout and `progress.csv` every `PROGRESS_INTERVAL_SEC` (2s).
|
||||
|
||||
### `bruteforce/p2pk_bruteforce_gpu.cu` (GPU, CUDA, single file)
|
||||
- Same algorithmic strategy as the CPU version (Jacobian batch add + Montgomery batch inversion), but GMP doesn't run on-device, so 256-bit field arithmetic mod the secp256k1 prime is hand-written (`u256` = 4×uint64 limbs, schoolbook multiply via `unsigned __int128`, fast reduction using `2^256 ≡ 2^32+977 (mod p)`). Verified bit-for-bit against `secp256k1_ec_pubkey_create` before trusting it (see conversation/test methodology — no separate test file is checked in).
|
||||
- Each CUDA thread is an independent search lane (own random starting privkey, own local batch state), same as a CPU thread — just thousands of them instead of ~11. Per-thread batch size is `GPU_EC_BATCH_SIZE` (128), smaller than the CPU's 256, to keep per-thread local-memory footprint (Jacobian batch arrays) bounded across tens of thousands of concurrent threads.
|
||||
- Scalar multiplication on-device is plain double-and-add (no wNAF/windowing/precomputed tables like libsecp256k1 has on CPU) — the main further-optimization opportunity if more speed is needed.
|
||||
- Host side still uses libsecp256k1 (CPU) only for one-time setup: precomputing G-multiples and validating/loading target keys into a host-sorted array + Bloom filter, both uploaded once to device memory. Device-side matching = Bloom filter check + binary search over the sorted target array.
|
||||
- `NVCC_ARCH` in the Makefile defaults to `sm_61` (Pascal) — must match the actual GPU (`make gpu-info` shows compute capability via `nvidia-smi`, then pick the matching `sm_XX`).
|
||||
- On WSL2, the NVIDIA driver comes from the Windows host (visible as `nvidia-smi` working out of the box) — only the CUDA Toolkit needs installing inside WSL, never a driver.
|
||||
|
||||
### `databases/scan_blockchain.py`
|
||||
- `P2PKBlockchainScanner` class wraps all mempool.space API access (`get_block_hash`, `get_block_transactions` with pagination, `check_utxo_status`) and SQLite persistence.
|
||||
|
||||
Reference in New Issue
Block a user