Skip to content

Repository files navigation

Note: The code of this project was built by gpt-5.6-sol high with little human supervision.

walgrind

CI crates.io

walgrind searches the unhardened BIP-32 index range of a Bitcoin output descriptor for a target address. It uses every available logical CPU by default and can derive wildcard public keys in parallel on an NVIDIA GPU.

Inputs contain public keys only. walgrind rejects xprv/tprv and WIF private keys, performs no network requests, and stops after verifying the first match.

It accepts either:

  • A public ranged BIP-380 output descriptor containing exactly two aligned BIP-389 multipath branches, such as /<0;1>/*.
  • A supported subset of BIP-388 wallet descriptor templates plus the ordered key-information vector. /** expands to /<0;1>/*.

Install

Install the latest release from crates.io with Rust 1.85 or newer. The default build uses the CPU backend:

cargo install --locked walgrind
walgrind --version

To build the development version from source instead:

git clone https://github.com/bigspider/walgrind.git
cd walgrind
cargo build --release
./target/release/walgrind --help

Use a release build for real searches. A full branch contains 2,147,483,648 indexes and can take a long time even across many CPU cores.

CUDA

CUDA support is opt-in and currently targets Linux. It requires an NVIDIA driver, a GPU with compute capability 7.5 or newer, nvcc, the CUDA runtime, ar, and a compatible C++ host toolchain:

cargo install --locked walgrind --features cuda
walgrind --list-cuda-devices

From a checkout, use:

cargo build --release --features cuda
./target/release/walgrind --list-cuda-devices

The build always embeds forward-compatible compute_75 PTX. When nvcc supports the architecture reported by the installed driver, it also embeds native SASS. A CUDA 12.0 toolkit can therefore run on Blackwell GPUs through driver JIT compilation even though it cannot emit native sm_120 code.

For cross-builds, set WG_CUDA_ARCH to a capability such as 89, 8.9, sm_89, or compute_89.

Quick start

This copy-pasteable example finds receiving index 1 in a three-index range:

walgrind \
  --descriptor 'wpkh([6738736c/84h/0h/2h]xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt/<0;1>/*)' \
  --branches receiving \
  --target bc1qx2pzftfcz0aqtq7du72m8nrtw96vcfyzhk237g \
  --end-index 2 \
  --threads 1

The stable stdout record is:

MATCH branch=receiving index=1 address=bc1qx2pzftfcz0aqtq7du72m8nrtw96vcfyzhk237g script_pubkey=0014328224ad3813fa0583cde795b3cc6b7174cc2482

Run with no arguments in a terminal for guided prompts:

walgrind

Inputs and branches

Direct descriptor input:

walgrind \
  --descriptor 'wpkh([d34db33f/84h/0h/0h]xpub.../<0;1>/*)' \
  --branches both \
  --target bc1q...

BIP-388 template input:

walgrind \
  --template 'wsh(sortedmulti(2,@0/**,@1/**))' \
  --key "[aaaaaaaa/48'/0'/0'/2']xpub..." \
  --key "[bbbbbbbb/48'/0'/0'/2']xpub..." \
  --branches receiving \
  --target bc1q...

Quote descriptor and template arguments because *, <, >, and # have special meanings in common shells. Keep the value on one physical line: line breaks inside shell quotes become descriptor characters and are invalid.

The first multipath choice is receiving and the second is change. With /<0;1>/*, these are branches 0 and 1 respectively.

The network is inferred from every xpub/tpub. --network mainnet or --network testnet asserts that inference; a conflict is rejected. The target must use the same network. Signet and regtest targets are not accepted.

CPU and CUDA searching

The CPU backend dynamically assigns small contiguous batches. It uses every logical CPU reported by the operating system unless constrained:

walgrind ... --threads 8
walgrind ... --threads 1

Select CUDA explicitly on a CUDA-enabled build:

walgrind ... --backend cuda --cuda-device 0

--threads is CPU-only. --cuda-device and --cuda-batch-size are CUDA-only; mixing backend-specific options is rejected.

The CUDA backend keeps BIP-32 derivation, descriptor hashing, Taproot construction, and full target comparison on the GPU. Only match and invalid derivation records return to Rust, and every reported match is rederived on the CPU before it is printed.

A process-wide 64 MiB generator table is built and validated at startup. If that allocation fails, the backend warns and uses the lower-memory w8 table. Automatic batch allocation starts at 524,288 indexes and halves to a minimum of 16,384. An explicit --cuda-batch-size is exact; allocation failure is fatal. CUDA finishes an already-launched batch before reporting a match or honoring cancellation.

The vendored arithmetic license, hashes, and provenance are under cuda/vendor/ultrafastsecp256k1.

Ranges, output, and resuming

The default range is 0..=2147483647. Use inclusive bounds for testing or splitting work:

walgrind ... --start-index 1000000 --end-index 1999999

Matches are the only stdout output. Progress, warnings, and the final summary go to stderr. This makes stdout directly usable by scripts: a successful search with empty stdout exhausted the requested range without a match.

On Ctrl-C, CPU workers finish claimed batches and CUDA finishes the in-flight batch before printing the next safe --start-index. Some completed work can be repeated after resuming, but no index is skipped. No checkpoint file is written.

Exit status is 0 after a match or complete exhaustion, 130 after Ctrl-C, 2 for invalid input, and 1 for fatal runtime failures.

Benchmarks

The reproducible benchmark compares one CPU thread, a configurable multicore count, every available CPU thread, and CUDA. It reports grinding speed, derived keys per second, and estimated one- and two-branch exhaustion time:

cargo run --release --features cuda --example cuda_bench

See benchmarks/README.md for the protocol and recorded RTX 5090 / CUDA 12.0 results.

Version 1 limitations

  • Public xpub/tpub input only; WIF, xprv, and tprv secrets are rejected.
  • Exactly two ranged multipath branches.
  • Address-bearing pkh, wpkh, sh, wsh, and tr descriptors supported by the Rust miniscript crate. raw, addr, combo, and bare P2PK are not.
  • BIP-388 placeholders must use @i/** or @i/<M;N>/*; optional vendor derivation patterns and musig key expressions are not implemented.
  • Mainnet and Bitcoin testnet targets only; signet and regtest are not.
  • CUDA failures and unsupported plans are fatal; the CLI never silently falls back to CPU.

About

Find your lost index. With multi-thread and CUDA support.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages