# FastScan GPU — README (EN) — Standalone Edition

A CUDA program for mass-scanning the secp256k1 (Bitcoin) private key space
and checking whether the corresponding address (compressed or
uncompressed) exists in your database of known addresses (hash160).

This document describes the **current** version of the program, including
the mmap-based address loading, the 24-bit prefix index accelerator, the
compressed/uncompressed/both scan mode selector, the fixed live-speed
counter, and the near-`2^256` edge-case fix. See section 9 ("Changelog /
fixed issues") for a summary of what changed and why.

---

## 1. Required files to run

All the files below must be located in the **same directory** you run the
program from (working directory when executing `./fastscan_opt_new ...`):

HOW TO RUN WITH SINGLE ADDRES: ./fastscan_opt_new 19MqGuwx8DWF7ekpfUjAoTLrp9UqRRAaxU 35 36 --mode=comp

### Source files (needed to compile)
| File | Description |
|---|---|
| `main_optimized1.cu` | Main program file (CUDA kernel + scanning logic + CPU driver) |
| `GPUSecp.h` | Configuration constants (GTable size, thread count, etc.) |
| `GPUSecp.cu` | secp256k1 EC operations on GPU |
| `GPUMath.h` | 256-bit modular arithmetic and EC point addition (Jacobian) |
| `GPUMath.cu` | Supplementary math functions |
| `GPUHash.h` | SHA256 / RIPEMD160 implementations on GPU |
| `GPUHash.cu` | Supplementary hash functions |
| `GPUGroup.h` | GPU kernel group management |

### Data files (REQUIRED for every run)
| File | Size | Description |
|---|---|---|
| `gtableX.bin` | 32 MB | Precomputed G-point table (X coordinate) — 16 chunks × 65536 values × 32 bytes |
| `gtableY.bin` | 32 MB | Precomputed G-point table (Y coordinate) |
| `addresses.bin` (your name) | depends on your database | File containing hash160 addresses to search for — **must be sorted ascending**, each record exactly 20 bytes (hash160) |

> **Note:** `gtableX.bin`/`gtableY.bin` are generated by a separate tool
> (`generate_gtable.cpp`) — if you don't have them, generate them first.
> The program will not run without them.

> **Note on the address database format:** the file must contain plain
> hash160 values (20 bytes, RIPEMD160(SHA256(pubkey))) laid out back to
> back, **sorted ascending byte-by-byte** — the program relies on this
> both for the fallback binary search and for building the 24-bit prefix
> index (see section 5). If the database is not sorted, lookups will
> silently give wrong results (false negatives).

### Files generated automatically while running
| File | Description |
|---|---|
| `found.txt` | Found keys/addresses are appended here (never overwritten) |
| `progress.txt` | Scan state (round, chunk, found count) — used with `--resume`, written at most every 10 minutes (see section 7) |

---

## 2. Compilation

```bash
nvcc -std=c++11 -O3 -arch=sm_89 -D_FORTIFY_SOURCE=0 -diag-suppress 1650 \
     -o fastscan_opt_new main_optimized1.cu -I. -lsecp256k1 -lssl -lcrypto -lcuda -lcudart
```

- `-arch=sm_89` — adjust to your GPU's architecture (e.g. `sm_86` for RTX
  30xx, `sm_89` for RTX 40xx). Check via `nvidia-smi` / NVIDIA CUDA Compute
  Capability documentation.
- Required system libraries: `libsecp256k1`, `libssl`/`libcrypto`
  (OpenSSL), CUDA Toolkit (nvcc, cudart).
- `main_optimized1.cu` also uses POSIX `mmap()`/`madvise()` (via `<sys/mman.h>`), so
  the program requires a POSIX-compatible environment (Linux, or WSL on
  Windows). It will not compile as-is on native Windows/MSVC.

---

## 3. Running and options

```bash
./fastscan_opt_new <addresses_file.bin> <start_bit> <end_bit> [--resume] [--mode=comp|uncomp|both]
```

| Argument | Required | Description |
|---|---|---|
| `addresses_file.bin` | Yes | Path to the sorted hash160 file (20 B/record) |
| `start_bit` | Yes | Starting bit of the private key range (e.g. `0`) |
| `end_bit` | Yes | Ending bit of the range (e.g. `65`) — key range is `[2^start_bit, 2^end_bit - 1]` |
| `--resume` | No | Resumes scanning from `progress.txt` (ignores the `start_bit`/`end_bit` passed on the command line and uses the saved state instead) |
| `--mode=comp` (alias `--mode=compressed`) | No | Only search for **compressed** addresses. Skips uncompressed hashing/lookup entirely on the GPU, which speeds up the scan. |
| `--mode=uncomp` (alias `--mode=uncompressed`) | No | Only search for **uncompressed** addresses. Skips compressed hashing/lookup entirely. |
| `--mode=both` | No | Search for **both** types (default if `--mode` is omitted — identical to old behavior). |

The `--mode` flag can appear anywhere after the first 3 positional
arguments, independently of `--resume`.

### Examples
```bash
# Scan keys in range [2^0, 2^5-1] = [1, 31], both compressed and uncompressed (default)
./fastscan_opt_new addresses.bin 0 5

# Scan a large range [2^60, 2^65-1]
./fastscan_opt_new addresses_unique.bin 60 65

# Resume interrupted scan (e.g. after Ctrl+C or a crash)
./fastscan_opt_new addresses_unique.bin 60 65 --resume

# Only look for compressed addresses (faster than "both")
./fastscan_opt_new addresses_unique.bin 0 65 --mode=comp

# Only look for uncompressed addresses
./fastscan_opt_new addresses_unique.bin 0 65 --mode=uncomp

# Combine --mode with --resume (order does not matter)
./fastscan_opt_new addresses_unique.bin 0 65 --resume --mode=comp
```

---

## 4. How scanning works (important to understand the output)

- The bit range **[start_bit, end_bit)** is **fixed** — the program does
  NOT automatically extend it.
- The range is split into `CHUNKS` parts (starting at 3563), each part
  sequentially scans a number of keys from its own starting point.
- After a full "round" completes (all chunks processed), the program
  **doubles the number of chunks** (3563 → 7126 → 14252 → ...) forever, to
  cover the same range with increasing density — the number of keys
  scanned sequentially per chunk shrinks proportionally, so **round
  duration stays roughly constant** (it does not grow exponentially,
  thanks to a work-budget cap described in the source comments).
- Chunks are mathematically guaranteed to **never overlap** until the
  scan becomes dense enough to check virtually every single key — the
  first and last chunk always cover the very start/end of the full range.
- Every key is checked **independently** as a compressed and/or
  uncompressed address (depending on `--mode`) — a hit on one type does
  not require a hit on the other.
- The program supports ranges up to the full 256 bits (the entire
  secp256k1 key space) thanks to BIGNUM arithmetic (OpenSSL) on the CPU
  side for range bookkeeping, and 256-bit integer math in the CUDA kernel.

### Reading the on-screen progress
```
🔁 Runda 3 | Chunks: 14252 | stride: ... | block_size (eff): 250000 (max: 1000000)
   Batch: 12/56 | chunki: 3104/14252 | 0.46 Gkeys/s | found: 0
```
- `Runda` — current round number (doubling happens after each full round)
- `Chunks` — target number of chunks for this round
- `chunki: X/Y` — chunks completed / total for this round (updated live)
- `Gkeys/s` — current scan speed (billions of keys per second), sampled
  with a 20 ms polling interval internally and printed at most once per
  second (see section 7 for why this matters)
- `found` — total number of hits found so far

---

## 5. The 24-bit prefix index (performance feature)

To speed up address lookups, the program builds a **24-bit prefix index**
on top of the sorted address database, both on the CPU (during startup)
and copied to GPU memory for use inside the CUDA kernel:

- For each of the 16,777,216 possible 24-bit prefixes (the first 3 bytes
  of a hash160), the index stores the `[lo, hi)` range in the sorted
  address array where all records with that prefix are guaranteed to
  live.
- Without the index, a lookup does a plain binary search over the whole
  database (`~log2(N)` comparisons, each one a random memory access in
  potentially many GB of GPU global memory — the dominant cost of the
  kernel).
- With the index, the kernel jumps directly to the matching bucket
  (typically a few dozen records for a ~600M-record database) and only
  binary-searches within that small bucket — cutting the number of random
  global-memory accesses roughly 5x.
- The index is built once at startup (a few seconds for hundreds of
  millions of records) and takes about **128 MB** of GPU memory
  (`16,777,217 × 8 bytes`).
- If GPU memory allocation for the index fails for any reason, the
  program **does not crash** — it prints a warning and falls back to the
  plain (slower) binary search automatically.

You will see this in the startup log:
```
📦 Budowanie i kopiowanie 24-bitowego indeksu prefiksowego na GPU...
📦 Budowanie 24-bitowego indeksu dla 606945376 adresów...
📊 Rozmiar indeksu: ~128 MB
✅ Indeks zbudowany: 16777216/16777216 prefiksów używanych
⏱️  Czas budowy: 3.8 s
✅ Indeks skopiowany na GPU (128 MB)
```

---

## 6. Memory handling: mmap instead of full RAM load

The main address database file (which can be many GB, e.g. 11+ GB) is
**memory-mapped** (`mmap`, read-only, `MAP_SHARED`) instead of being fully
read into a `std::vector` in RAM:

```
📂 Mapowanie (mmap) pliku adresów: addresses.bin
📊 Hash-y: 606945376
📊 Rozmiar pliku: 11 GB (mmap, nie kopiowany do RAM)
```

- The file is opened **strictly read-only** (`O_RDONLY` / `PROT_READ`) —
  the program can never modify or corrupt your original address file.
- Data is paged in lazily from the OS page cache/disk instead of being
  duplicated in process memory, which makes startup faster and reduces
  peak RAM usage.
- The mapped data is used both to build the 24-bit prefix index (CPU) and
  to copy the raw hash160 array to GPU memory (`cudaMemcpy` straight from
  the mapped pointer).
- **Safety recommendation:** although mmap here is read-only and cannot
  corrupt the source file, it is still good practice to keep a backup
  copy of your address database before running large/unattended scans,
  in case of unrelated disk/filesystem issues.

---

## 7. Progress saving and logging frequency (performance notes)

Two behaviors were tuned to minimize overhead during long scans:

- **`progress.txt` is now saved at most once every 10 minutes** during a
  round (instead of after every single kernel batch, which could be
  hundreds/thousands of times per round for deep rounds with many small
  chunks). A save is also always performed once at the end of every
  round. This means that, in the worst case, resuming with `--resume`
  after an unclean shutdown may replay up to ~10 minutes of already-done
  work — a reasonable trade-off for much less disk I/O overhead during
  normal operation.
- **Live speed/progress printing is throttled to ~once per second**, but
  the underlying GPU-kernel-completion **polling** runs every 20 ms. This
  distinction matters: an earlier revision that slowed down the polling
  interval itself (to reduce terminal I/O) caused the *measured elapsed
  time* to include artificial "dead time" while waiting for the next
  poll, which made the computed `Gkeys/s` value drop towards `0.00` in
  later rounds with many small/fast kernel batches — even though the GPU
  itself was working at full/normal speed. Decoupling "how often we check
  if the kernel finished" from "how often we print" fixed this: polling
  stays fast (accurate timing), printing stays infrequent (low terminal
  I/O overhead).

---

## 8. Results

Every hit is appended to `found.txt` in this format:
```
KEY: 0000...0001abcd
TYP: COMPRESSED
ADDR: 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
---
```

`TYP` will only ever be `COMPRESSED` or `UNCOMPRESSED` depending on which
kind of address actually matched — never both in a single entry, even
when `--mode=both` is active and a given private key happens to match on
both address types (in that case two separate entries are written, one
per type).

---

## 9. Changelog / fixed issues (for users of older builds)

If you are updating from an older copy of this program, the following
issues have been fixed and features added:

1. **Crash at the very end of the key space** (`illegal argument:
   !secp256k1_fe_is_zero(&ge->x)`, `Aborted (core dumped)`) — this
   happened specifically when scanning up to bit 256 (e.g.
   `./fastscan_opt_new addresses.bin 255 256`), because the secp256k1 curve
   order `n` is extremely close to `2^256` (difference of only
   `~2^129`), so a small sliver of the raw 256-bit key space near the top
   (`k >= n`) produced mathematically valid EC points/addresses but
   invalid scalars for libsecp256k1's `secp256k1_ec_pubkey_create`
   (which requires `0 < k < n`). The program now detects this, reduces
   the found key modulo `n` (`k mod n`, the actual usable private key
   for that address), and proceeds normally instead of crashing.
2. **24-bit prefix index moved to the GPU** for faster address lookups
   (see section 5).
3. **mmap-based address file loading** instead of a full RAM copy (see
   section 6).
4. **`progress.txt` write throttling** (10-minute interval instead of
   every batch) to reduce disk I/O overhead (see section 7).
5. **Live speed counter fix** — previously could incorrectly drop to
   `0.00 Gkeys/s` in later, fast-running rounds due to an overly long
   polling sleep interval; now accurate at all times (see section 7).
6. **`--mode=comp|uncomp|both` selector** added, letting you skip
   unnecessary hashing/lookup work for the address type you don't care
   about (see section 3).

---

## 10. Benchmark / Real-world performance

**RTX 4090 (sm_89), standalone scan with SHA256+RIPEMD hashing:**

| Mode | Speed |
|------|-------|
| `--mode=comp` (compressed only) | **5.2 GH/s** |
| `--mode=both` (compressed + uncompressed) | ~2.5 GH/s |
| `--mode=uncomp` (uncompressed only) | ~3.5 GH/s |

> **Important:** These are **real, standalone speeds** measured on a single
> RTX 4090 with a full database and with SHA256+RIPEMD hashing active on every
> key. No pool, no network — just pure local scan. A 5.2 GH/s score means the
> tool is scanning **5.2 billion keys per second** — that's checking 5.2 billion
> private keys and hashing each one through SHA256+RIPEMD **every second**.

Older standalone Google Drive binaries (not updated) reach ~3.5 GH/s in the same
test — this optimized build from source is ~50% faster.

---

## 11. Hardware/software requirements

- NVIDIA GPU with CUDA support (tested on RTX 4090, `sm_89`)
- CUDA Toolkit 12.x (nvcc)
- `libsecp256k1`, `OpenSSL` (dev headers + libraries)
- Linux or WSL (POSIX `mmap`/`madvise` support required)
- Enough VRAM to hold: the address database (copied once to GPU) +
  GTable (64 MB) + the 24-bit prefix index (~128 MB) + small output
  buffers. The address database itself is **not** fully loaded into host
  RAM anymore (see section 6) — it is memory-mapped, so host RAM
  requirements are modest (roughly a few hundred MB beyond the OS page
  cache).
## donate: bc1qps62cyk9f9unmdkc9k3ccj9e2h8ywfhg2j53ec
