← Back to Home

From VanitySearch to 50 GH/s – Inside the 253-256 Scanner

Engineering Performance CUDA

This is the story of how a CUDA kernel originally designed for vanity address generation was transformed into a 50 GH/s scanner for the 253-256 forgotten Satoshi wallets. The code is open-source, the math is public — the only novelty is the integration.

The Starting Point: VanitySearch

VanitySearch is a well-known CUDA tool that finds vanity Bitcoin addresses. On an RTX 4090, it achieves around 3-4 GH/s. The logic is simple: generate a private key, compute the public key, hash it to an address, and compare it to a pattern. It works, but it does a lot of unnecessary work for our use case.

The 253-256 campaign doesn't need vanity patterns. It needs to check whether a generated public key matches any of the 734,000+ public keys in the database. That's a completely different problem — and one that can be solved much faster.

X-Only Pubkey Lookup

Instead of hashing the public key (SHA-256 + RIPEMD-160) and encoding it (Base58 or Bech32), we compare the X coordinate of the compressed public key directly. For secp256k1, the X coordinate uniquely identifies the key for our purposes.

In the code, this is implemented in the _BinarySearchX32Only function:

__device__ __forceinline__ bool _BinarySearchX32Only( const uint8_t* buffer, uint64_t lo, uint64_t hi, const uint8_t* targetX32, const uint32_t* L1, const uint32_t* L2, const uint64_t* qx_use) { if (!_BitmapCheck32(L1, L2, qx_use)) return false; while (lo < hi) { uint64_t mid = (lo + hi) / 2; const uint8_t* rec = buffer + mid * 65; int c = _CmpX32(rec + 1, targetX32); if(c == 0) return true; else if(c < 0) lo = mid + 1; else hi = mid; } return false; }

This removes SHA-256 and RIPEMD-160 entirely from the hot path. The database contains 65-byte records (04 || X || Y), and we compare only the 32 bytes of X.

GLV Endomorphism — 6 Keys per Point Addition

The secp256k1 curve has an endomorphism (λ, β) that allows us to compute two additional public keys from a single point addition. The constants are defined in the code:

static const char* LAMBDA_HEX = "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72"; static const char* LAMBDA2_HEX = "ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce";

For every point P = k·G, we can compute:

In the kernel, this is implemented with #if USE_ENDO:

#if USE_ENDO uint64_t qx2[4], qx3[4]; _ModMult(qx2, qx_use, (uint64_t*)_beta); _ModMult(qx3, qx2, (uint64_t*)_beta); // Check all three X coordinates in parallel #endif

One point addition → 6 lookups into the database. With the GPU sustaining ~8-9 Gpoint/s, this gives ~48-54 Gkeys/s effective.

Batch Inversion — Reducing Memory Bottlenecks

The bottleneck is memory access, not computation. Each lookup is a random access into a 128 MB prefix index. To amortize this, the kernel processes points in batches:

#define GROUP_BATCH 40 // ... for(int i = 0; i < n; i++) { // Batch invert all slopes // Then check all X coordinates in parallel }

This reduces global memory reads and increases effective throughput. The 24-bit prefix index is stored in shared memory (LDS) where possible.

24-Bit Prefix Index — 128 MB on GPU

Instead of a full 160-bit hash index, we use a 24-bit prefix (first 3 bytes of the X coordinate). The index is built on the CPU once and uploaded to the GPU:

vector buildPrefixIndex24() { vector index(16777217, 0); // Count prefixes for (uint64_t pos = 0; pos < count; pos++) { uint32_t p = get_prefix24(base + pos * 65); index[p]++; } // Convert to cumulative offsets // ... }

The index is 16,777,217 entries of 64-bit — ~128 MB. This fits easily on any modern GPU and reduces the search space from O(log n) to O(1) for the prefix lookup.

DEDUP Optimization — No Duplicate Work

The scanner uses a multi-round approach. Each round doubles the number of chunks to avoid scanning the same range twice. The kernel uses grid_mult and grid_off to handle this:

uint64_t grid_mult = (round_idx == 1) ? 1 : 2; uint64_t grid_off = (round_idx == 1) ? 0 : 1;

This ensures that when the number of chunks doubles, every second chunk is the same as the previous round. The grid_off offset shifts the starting point to cover the missing chunks. No duplicate work.

Edge Cases and TDR Timeouts

The secp256k1 curve has a prime order n. Edge cases (k=0, λ·k mod n = 0, etc.) are handled in the kernel:

bool isPrivOne = (priv4[0] == 1 && priv4[1] == 0 && priv4[2] == 0 && priv4[3] == 0); if(isPrivOne) { // G is hardcoded to avoid point multiplication for k=1 }

The biggest challenge was TDR (Timeout Detection and Recovery) on Windows. Long-running kernels (>2 seconds) trigger the driver to reset the GPU. The solution: split the workload into smaller chunks, with a maximum of ~4 million keys per kernel launch, and carefully measure execution time.

Multi-GPU Support

The scanner supports multiple GPUs with two modes:

The split logic divides RLEN (total range length) by the number of GPUs:

if (split_gpu && total_gpus > 1) { BN_div(chunk_size, nullptr, total_range, gpu_count, bnctx); BN_mul(my_start, gpu_idx_bn, chunk_size, bnctx); BN_add(my_end, my_start, chunk_size); // ... }

Verification — Tested on Known Ranges

The scanner has been tested and verified on multiple known ranges:

All tests confirm the scanner works as expected.

Live Production — With Additional Security

The scanner is currently running in production. For security reasons, the integration details — including the split-key mechanism and server-side logic — are not disclosed. This is to prevent anyone from extracting keys during the finding process.

It's not magic. It's engineering.

Every technique used here has been documented for years:

  • GLV endomorphism — Hal Finney, 2003
  • Batch inversion — Used in many crypto libraries
  • 24-bit prefix index — Standard database optimization

What is new is the integration: combining them into a single, stable, production-ready binary that runs on any CUDA GPU from GTX 9xx to RTX 50xx.

What's Next?

The current speed is 50+ GH/s on RTX 4090. Future work includes:

🔗 GitHub: FastscanGPU 253-256 files

📖 Read more: Puzzle 71 Visualization · Pool Rules · How to Start · Security

© 2026 SatoshiPool.org · Home · Telegram