Skip to content

Repository files navigation

solgrind

The fastest Solana vanity address grinder on Apple Silicon. No CUDA required.

solgrind is an ed25519 vanity grinder that runs on the Apple GPU (Metal). It searches for Solana addresses (base58-encoded ed25519 public keys) that match a prefix or suffix, and writes the matching keypair to a standard Solana JSON file you can drop straight into ~/.config/solana/id.json.

On an Apple M4 Max it sustains 13.2 million attempts/sec end-to-end, which is 10.3× faster than the official solana-keygen grind on the same machine.

Quick start

git clone <repo> && cd solgrind

# Find an address starting with "So" using the GPU (Metal). Base58 excludes 0/O/I/l.
cargo run --release --features metal-gpu --bin solgrind -- grind --prefix So --gpu

# CPU fallback (no --gpu flag, no metal-gpu feature needed)
cargo run --release --bin solgrind -- grind --prefix So

# Throughput check
cargo run --release --features metal-gpu --bin eval-perf

The grinder runs until a match is found, then writes vanity.json (override with -o). On a 4-character prefix you should expect a hit in a couple of seconds on GPU or ~20 seconds on CPU.

Benchmarks

Sustained attempts/sec for the full pipeline (RNG → SHA-512 → ed25519 scalar mult → point compress → base58 prefix check). All "real" numbers are measured locally.

Apple Silicon (Apple M4 Max, 40-core GPU, 16 CPU cores)

Tool Backend Rate × vs solana-keygen Source
solana-keygen grind CPU, 16 thr 1.28 M/s 1.00× measured
solgrind (CPU) CPU, 16 thr 1.59 M/s 1.24× measured
solgrind (Metal GPU) M4 Max iGPU 13.23 M/s 10.33× measured

NVIDIA reference (different hardware, published claims)

For absolute context only. CUDA grinders do not run on Apple Silicon.

Tool Backend Rate Notes
Solanity CUDA, RTX 3080 ~40–70 M/s Per project README and community reports; ~3–5× solgrind.
CUDA grinders RTX 4090 (est.) ~100–150 M/s Linear scaling estimate from FP32 throughput; ~8–11× solgrind.

Caveats: the NVIDIA rows are pulled from public project claims and FP32-scaling estimates, not numbers we re-measured. The RTX 4090 has roughly 8× the FP32 throughput of the M4 Max GPU, so a well-tuned CUDA grinder will outpace solgrind on dedicated NVIDIA hardware, but on a Mac solgrind is the option that runs.

What this means in practice

You have… Best choice
MacBook / Mac Studio / Mac mini solgrind --gpu (10× faster than the official tool)
Linux + NVIDIA GPU Solanity or another mature CUDA grinder
Linux without a GPU solgrind CPU build (Rayon-parallel, ~1.6 M/s)

How it works

The hot path is seed → SHA-512 → clamp scalar → scalar · BasePoint → compress → check. On a 16-core M4 Max CPU this tops out at ~1.6 M attempts/sec because each thread runs dalek's constant-time scalar multiplication serially. The GPU breaks this ceiling by running thousands of derivations in parallel and applying four stacked algorithmic wins to each one.

1. Fixed-base 8-bit windowing

The naive scalar multiplication loops 256 times over the bits of the scalar, doubling the accumulator each step and adding the basepoint on set bits. We precompute a table of d · 256^w · B for every byte position w ∈ 0..32 and every byte value d ∈ 1..255 (32 windows × 255 entries × 160 bytes ≈ 1.27 MB, generated once at startup, lives in device memory). Each thread now loops 32 times instead of 256, doing a single table lookup plus point addition per iteration. That alone is the ~4× speedup on the kernel.

2. Niels-form precomputed points

The standard extended-twisted-Edwards point addition takes 10 field multiplications. With precomputed points (the basepoint multiples are known up front) we can store each table entry as (y − x, y + x, 2·d·x·y) instead of (X, Y, Z, T). The mixed addition then takes 7 muls instead of 10, a 30% reduction in the inner loop's arithmetic, and the table also shrinks by 25% (3 field elements per entry vs 4). This is the same trick dalek uses for its AffineNielsPoint representation, ported into the MSL kernel. Stacked on top of windowing it's another ~1.22×.

3. Threadgroup-cooperative batch inversion

The final point_compress step requires inverting the Z coordinate via Fermat's little theorem (z^(p-2) mod p), which costs ~254 field multiplications per thread. With windowing the scalar mult is only ~224 muls per thread, so the per-thread inversion was eating ~50% of the work.

We share one inversion across 64 threads using a Hillis–Steele prefix scan in threadgroup memory: collect every thread's Z into shared memory, parallel-scan to get running products, invert the final product once on thread 0, then walk back to recover each thread's individual inverse. Per-thread invert cost drops from ~254 muls to ~5 muls (one shared invert plus a per-thread combine), giving another ~1.17×.

4. Persistent grinder context

The host-side grind loop used to allocate Metal buffers on every iteration, memcpy seeds from a Vec into device memory, and read pubkeys back. That ate ~40% of wall time at the kernel's measured 13 M/s.

MetalBackend::make_grinder(batch_size) now pre-allocates the input/output buffers once. The grind loop writes seeds straight into the shared input buffer via grinder.seeds_mut() (no memcpy), dispatches, then reads pubkeys directly from the output buffer. RNG fill and matcher checks run in parallel across CPU cores via Rayon. End-to-end throughput now matches the kernel-isolated bench: ~7.5 M/s → 13.23 M/s, a 1.76× host-side win that closes the gap entirely.

Cumulative effect

Starting from the per-bit double-and-add MSL kernel (2.4 M/s on M4 Max), the optimizations stack as:

2.40  M/s   per-bit table baseline                 (1.5× CPU)
6.77  M/s   + 4-bit window                        (4.2× CPU)
10.02 M/s   + 8-bit window                        (6.3× CPU)
11.19 M/s   + threadgroup batch invert            (7.0× CPU)
13.04 M/s   + Niels precomputed points            (8.2× CPU, kernel only)
13.23 M/s   + persistent grinder ctx, end-to-end  (8.3× CPU baseline / 10.3× official)

One thing that didn't work: field26 (10×26-bit limbs)

The textbook advice for 32-bit GPUs is to use ref10's field26 representation (10 × 26-bit signed limbs) instead of dalek's field51 (5 × 51-bit unsigned limbs). On Apple GPU this lost by 28×. Why: Metal's MSL compiler emulates i64 × i64 as a multi-instruction sequence regardless of operand bit-width, so ref10's 100 schoolbook multiplications never beat field51's 25 mulhi/mullo pairs, which the compiler maps cleanly to the GPU's native 32×32→64 multiply instruction.

The kernel is kept (gated as ed25519_derive_table_26) as a documented dead-end. See docs/CHANGELOG.md for the measurements.

Status & limits

  • What ships: end-to-end Solana vanity grinding on Metal GPU, byte-exact vs dalek across 100 000+ random scalars (eval-correctness test suite). 117 unit tests pass, 0 ignored. CLI matches solana-keygen grind's output format.
  • Practical ceiling on M4 Max: ~13–14 M/s. The remaining ~17% of per-thread work is the in-kernel SHA-512, which uses MSL's emulated u64 path. A u32-pair vectorized SHA-512 rewrite could push this closer to 16 M/s but is left for v0.6.
  • Constant-time? No. This grinder is deliberately not constant-time: it runs if (digit != 0) add over each scalar byte, branching on the secret. This is fine for vanity grinding (the scalar is random and immediately discarded if it doesn't match), but do not reuse this kernel for signing.

Repository layout

solgrind/
├── src/
│   ├── cpu/       reference ed25519 implementation (correctness oracle)
│   ├── metal/     GPU host code + MetalGrinder API (gated by metal-gpu feature)
│   ├── matcher/   prefix/suffix + byte-range pruning
│   ├── grind.rs   CPU rayon + GPU grind loops
│   └── bin/       solgrind CLI + eval-correctness + eval-perf
├── kernels/       MSL kernel source (point arithmetic, batch invert, derive)
├── benches/       criterion benchmarks (CPU + GPU)
├── eval/          correctness and perf eval harness
└── docs/          CHANGELOG.md (per-version optimization notes), findings.md

Reproducing the benchmarks

# Tier 1: unit tests (~3 s)
cargo test --release --features metal-gpu

# Tier 2: correctness eval (~3 s; 115k+ comparisons vs dalek)
cargo run --release --features metal-gpu --bin eval-correctness

# Tier 3: throughput (CPU + GPU side-by-side, ~30 s)
cargo run --release --features metal-gpu --bin eval-perf

# Criterion micro-benches for the GPU derive variants
cargo bench --features metal-gpu --bench ed25519_ops -- metal_derive

solana-keygen grind comparison:

# 50 matches of a 3-char case-sensitive prefix (~9.76M expected attempts).
# Time it and divide.
time solana-keygen grind --starts-with sun:50 --num-threads 16 --no-bip39-passphrase

# Our measurement on M4 Max: 173M / 135s = 1.28 M/s

License

MIT

Credits

  • curve25519-dalek: the correctness oracle and the reference implementation for field51, HWCD point arithmetic, and the Niels precomputed-point form.
  • Bernstein, Lange et al.: the underlying ed25519 algorithms (RFC 8032, HWCD 2008).

About

The fastest Solana vanity address grinder on Apple Silicon

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages