Matrix-Search scans a range of secp256k1 private keys on NVIDIA GPUs, looking for the key that belongs to a given compressed public key. It is built around the Bitcoin puzzle ranges. A CUDA engine scans one chunk of keys per run. A Go pool server splits the target range into chunks, hands them to GPU workers over WebSocket, keeps progress in SQLite so a restart loses no work, checks every reported key, and serves a web dashboard. A Go miner runs one engine process per GPU and reports speed and results back to the server.
Browser Pool server (Go) GPU host (Go + CUDA)
+----------------+ HTTP +----------------------------+ WS +-----------------------------+
| dashboard.html |------>| /api/stats /api/config |<----->| pool-miner |
| polls every | | /api/terminate | /mine | one connection per GPU |
| 1.5 s | | chunk queue, key check | | runs per chunk: |
+----------------+ | state.db (SQLite) | | kangaroo <start> <end> |
| current_target.json | | <pubkey> <gpu> |
+----------------------------+ +-----------------------------+
- CUDA engine (
kangaroo.cu,secp256k1_math.cuh): scans[start, end]on one GPU and printsSPD:<keys checked>while it runs andHIT:<private key>if it finds the key. - Pool server (
cmd/pool-server): holds the current target, cuts it into chunks of 10^10 keys, assigns them to miners, records progress and found keys, and serves the dashboard and its JSON API. - Miner (
cmd/pool-miner): detects GPUs withnvidia-smi -L, opens one WebSocket connection per GPU, runs the engine for each chunk it receives and forwards speed and the result. - Dashboard (
dashboard.html): deploys and stops targets, shows pool speed, connected workers, progress, estimated time remaining, the server log and found keys.
- secp256k1 arithmetic on the GPU: 256-bit field operations with the fast reduction for p = 2^256 - 2^32 - 977, Jacobian point doubling and mixed addition.
- One scalar multiplication per thread, then one point addition per key. The match test compares X with x * Z^2, so no modular inversion runs in the inner loop.
- Distributed work over WebSocket, with one worker per GPU on each host.
- Checkpointing in SQLite: a restarted server resumes the range, and chunks that were not finished are assigned again.
- Server-side check of every reported key against the target public key before it is stored.
- Dashboard presets for the unsolved puzzles #135, #140, #145, #150, #155 and #160 (also listed in
puzzles_unsolved.txt).
- NVIDIA GPU and the CUDA Toolkit (
nvcc) to build and run the engine. On Windows,nvccalso needs the MSVC compiler. The author builds with CUDA 13.1 on Windows. - Go 1.25 or newer for the server and the miner.
nvidia-smion the miner'sPATHto start one worker per GPU. Without it the miner starts one worker on device 0.
The pool server does not need a GPU.
git clone https://github.com/Hitice/Matrix-Search.git
cd Matrix-Search
# CUDA engine: Linux
nvcc -O3 -o kangaroo kangaroo.cu
# CUDA engine: Windows (Developer Command Prompt with MSVC)
nvcc -O3 -o kangaroo.exe kangaroo.cu
# Pool server and miner
go build -o pool-server ./cmd/pool-server
go build -o pool-miner ./cmd/pool-minerAdd -arch=sm_XX to the nvcc command to build for your GPU's compute capability.
Start the server from the repository root. It reads dashboard.html and writes state.db, current_target.json and hit.txt in its working directory.
./pool-serverOpen http://localhost:8080, pick a preset or type a target, and click Start search. A target can also be deployed through the API:
curl -X POST http://localhost:8080/api/config -d '{
"target_address": "1LHtnpd8nU5VHEMkG2TMYYNUjjLc992bps",
"target_public_key": "030d282cf2ff536d2c42f105d0b8588821a915dc3f9a05bd98bb23af67a2e92a5b",
"range_start": "20000000",
"range_end": "3fffffff"
}'That target is puzzle #30, which is already solved (private key 0x3d94cd64). Its range holds 2^29 keys, a single chunk, which makes it a quick end-to-end test.
Start a miner on each GPU host, next to the engine binary:
./pool-miner -pool ws://<server-host>:8080/mine -wallet rig01Each GPU connects as rig01_GPU0, rig01_GPU1, and so on. The engine can also run on its own:
./kangaroo 20000000 3fffffff 030d282cf2ff536d2c42f105d0b8588821a915dc3f9a05bd98bb23af67a2e92a5b 0| Binary | Flag | Default | Meaning |
|---|---|---|---|
pool-server |
-addr |
:8080 |
Listen address for the dashboard, the API and /mine |
pool-miner |
-pool |
ws://localhost:8080/mine |
Pool server WebSocket URL |
pool-miner |
-wallet |
AnonMiner |
Worker name shown on the server |
pool-miner |
-engine |
./kangaroo (./kangaroo.exe on Windows) |
Path to the CUDA engine |
| Method | Path | Description |
|---|---|---|
GET |
/api/stats |
Target, pool speed (keys/s), connected workers, keys scanned, last 300 log lines, last 20 found keys |
POST |
/api/config |
Validate and deploy a target (target_address, target_public_key, range_start, range_end); returns 400 with the reason when invalid |
POST |
/api/terminate |
Stop the current target |
POST |
/api/clear_logs |
Clear the server log buffer |
GET |
/mine |
WebSocket endpoint for miners |
The server sends {"cmd":"IDLE"} while there is nothing to assign, or {"cmd":"WORK","start":"<hex>","end":"<hex>","pubkey":"<hex>"}. While the engine runs, the miner sends {"event":"PROGRESS","speed":<keys/s>} about every two seconds. It closes the chunk with exactly one of {"event":"DONE"}, {"event":"HIT","priv":"<hex>"} or {"event":"FAILED","msg":"..."}.
Progress is stored per target public key and range in state.db:
search_progresskeeps the cursor, which is the first key of the next chunk to cut, and the number of keys in chunks that miners reported as fully scanned. The dashboard progress bar and time estimate come from that count.chunksholds every chunk that was cut but not finished. A chunk isassignedwhile a miner works on it. If the miner reportsFAILED, reports a key that does not match, or disconnects, the chunk goes back topending. On startup the server moves everyassignedchunk back topending, because no miner from the previous run is still working on it.- Pending chunks are handed out before new chunks are cut, so no key of the range is skipped.
- A chunk leaves the table when the miner reports
DONE(its keys are added to the scanned count) or a verifiedHIT. - The target stops on its own when a verified key is found, or when the cursor has passed the end of the range and no chunk is left in the table.
Deploying the same public key and range again resumes where it stopped. A different range for the same key is tracked separately.
- The search is a sequential scan, so the time grows linearly with the size of the range. Despite the file name,
kangaroo.cudoes not implement Pollard's kangaroo. For the unsolved puzzles the dashboard shows the estimate as an order of magnitude, for example "at least 10^24 years" for puzzle #135 at 155 M keys/s. - The server has no authentication. Anyone who can reach it can deploy or stop targets and read found keys from
/api/stats, and the WebSocket endpoint does not check theOriginheader. Run it on a trusted network, bind it to localhost with-addr 127.0.0.1:8080, or put it behind a reverse proxy that authenticates. - Stopping a target takes effect at the next chunk boundary: miners finish their current chunk first.
- The speed reported by the engine is approximate, since thread 0 prints the shared counter while other threads of the same launch may still be running.
The dashboard with the puzzle #30 target from current_target.json.example loaded and no miner connected. Speed and keys scanned fill in once a miner with a GPU joins.
| Path | Contents |
|---|---|
kangaroo.cu |
Engine entry point, search kernel and host loop |
secp256k1_math.cuh |
Field and point arithmetic for the GPU |
cmd/pool-server/ |
Pool server: HTTP API, WebSocket hub, SQLite checkpoint (store.go), key check (secp256k1.go) and tests |
cmd/pool-miner/ |
Miner: GPU detection, engine runner, WebSocket client |
dashboard.html |
Single-file web dashboard served by the pool server |
current_target.json.example |
Example target (puzzle #30) |
puzzles_unsolved.txt |
Ranges, addresses and public keys of the puzzles in the dashboard presets |
This project is for research and education. The puzzle addresses are a public challenge. Use the tool only on keys you are entitled to search for.
