https://github.com/eandersson/erikslund-solo-pool
Bitcoin solo mining pool
https://github.com/eandersson/erikslund-solo-pool
bitcoin
Last synced: 12 days ago
JSON representation
Bitcoin solo mining pool
- Host: GitHub
- URL: https://github.com/eandersson/erikslund-solo-pool
- Owner: eandersson
- License: gpl-3.0
- Created: 2026-06-03T12:34:12.000Z (19 days ago)
- Default Branch: main
- Last Pushed: 2026-06-05T21:53:47.000Z (16 days ago)
- Last Synced: 2026-06-05T23:14:09.934Z (16 days ago)
- Topics: bitcoin
- Language: Python
- Homepage:
- Size: 363 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# erikslund-solo-pool
Two independent implementations of a **Bitcoin solo mining pool** -- one in modern
**C++**, one in **free-threaded Python** -- sharing a config format, an
HTTP/Prometheus API, and a Docker regtest harness. Both speak Stratum v1, build
block templates from `bitcoind`, validate miner shares, and submit solved blocks.
*Solo* means every block a miner finds pays **that miner's address in full**
(minus an optional donation); the pool coordinates and distributes work, it does
not split rewards across participants.
> WARNING: **This builds and submits real Bitcoin blocks.** Run it on regtest/signet/testnet with your actual hardware first.
## Two implementations, one behavior
| | [`cpp/`](cpp/) | [`python/`](python/) |
| --- | --- | --- |
| Language / runtime | C++26 (GCC 16.1) | Free-threaded CPython 3.14t (no-GIL) |
| Concurrency | epoll reactor, one thread per core | N asyncio event loops (`SO_REUSEPORT`), one per core |
| Share hashing | Vendored Bitcoin Core SHA-256 -- SSE4 / AVX2 / SHA-NI runtime dispatch | `hashlib` (OpenSSL) |
| JSON | simdjson (parse) | msgspec |
| Allocator | mimalloc | CPython default |
| Leans toward | Throughput, memory, latency | Readability, hackability |
## Quick start
**Real network** -- Docker Compose stacks in [`deploy/`](deploy/), with or without
an embedded Bitcoin Core.
**Bring your own Bitcoin node** -- these stacks run the pool only and bind-mount
its config + data from the host. The pool runs as **UID/GID 1000**, so create the
host dirs owned by `1000:1000` (Docker does not chown bind-mounts) or it can't
persist stats or spool found blocks:
```sh
sudo mkdir -p /opt/erikslund-pool/etc /opt/erikslund-pool/data /opt/erikslund-pool/logs
sudo cp conf/pool.yml /opt/erikslund-pool/etc/pool.yml
sudo chown -R 1000:1000 /opt/erikslund-pool
docker compose -f deploy/docker-compose.cpp.yml up -d --build # C++ pool
docker compose -f deploy/docker-compose.python.yml up -d --build # Python pool
```
## Configuration
One YAML file, [`conf/pool.yml`](conf/pool.yml), validated by
[`conf/pool.schema.json`](conf/pool.schema.json) and read by **both** pools:
```yaml
bitcoin_nodes:
- address: bitcoind:8332 # extra entries are failover nodes
username: erikslund
password: CHANGE_ME_before_deploying
stratum_listen: [0.0.0.0:3333] # multiple entries bind multiple ports
api_listen: [127.0.0.1:7777] # status + /metrics -- loopback default; override to expose
initial_difficulty: 10000 # vardiff adjusts from here
minimum_difficulty: 1
zmq_block_endpoint: tcp://bitcoind:28332 # instant new-block work
```
| Port | Purpose |
| --- |--------------------------------------------------------------------------------|
| `3333` | Stratum V1 -- miners connect here |
| `7777` | HTTP status JSON + Prometheus `/metrics` (`/health`, `/status`, `/stats/*`) |
## Benchmarks
[`tools/bench/`](tools/bench/) measures the **share-validation hot path** -- parse
`mining.submit` -> rebuild coinbase/merkle/header -> double-SHA256 -> target
compare -- on a private regtest chain. A synthetic Stratum client
([`stratum_bench.py`](tools/bench/stratum_bench.py)) floods structurally-valid,
always-above-target shares (no proof-of-work, so no blocks), exercising the
pool's *full* validation path. Both pools run the identical
[`tools/bench/bench.yml`](tools/bench/bench.yml) and the same generator.
Measured on a **32-core x86-64** host, Docker, 30 s window after a 5 s warmup
(`bash tools/bench/run_bench.sh`, Release build). The generator hashes each share too,
so it's CPU-bound: throughput at saturation (16 workers) is *generator-bound* for
both pools (~310-320k/s), making the **per-core** and **memory** columns -- not
raw shares/s -- the efficiency signal.
| impl | shares/s | per core | p50 ms | p95 ms | p99 ms | CPU % | peak RSS |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| **C++** | 320,990 | **~47,000** | 12.4 | 17.0 | 18.9 | 682 | **242 MiB** |
| **Python** | 305,433 | ~20,300 | 14.6 | 34.7 | 70.3 | 1504 | 945 MiB |
Both clear ~310k shares/s, but C++ does it on **~6.8 cores vs ~15** -- about
**2.3x the throughput per core**, with **~3.9x less memory** and a far tighter
tail (**p99 19 ms vs 70 ms**).
**Memory at connection scale** -- idle connections (each does a real
`mining.subscribe` + `mining.authorize`, then holds), pool RSS via `docker stats`
([`tools/bench/connscale.sh`](tools/bench/connscale.sh)):
| idle connections | C++ RSS | Python RSS |
| --- | ---: | ---: |
| 50 | 92 MiB | 426 MiB |
| 64,000 | **318 MiB** (~5 KiB/conn) | **1,332 MiB** (~20 KiB/conn) |
Both hold 64k idle connections without dropping any -- each connection is a
socket, so raise the deploy's `nofile` / `max_clients` to match.
## Layout
| Path | What |
| --- | --- |
| [`cpp/`](cpp/) | C++26 pool -- sources, tests (doctest), Docker build + smoke test |
| [`python/`](python/) | Free-threaded Python pool -- package `erikslund_pool`, tests, Docker image |
| [`conf/`](conf/) | Shared `pool.yml` + JSON-Schema |
| [`tools/`](tools/) | Dev & test tooling -- `regtest/` harness (bitcoind + miner), `bench/` (share-validation benchmark), CPU miner, fake bitcoind, status page |
| [`deploy/`](deploy/) | Production Compose stacks (embedded / external bitcoind) |
## License
Licensed under the **[GNU General Public License v3.0 or later](LICENSE)**. The
vendored Bitcoin Core SHA-256 in [`cpp/third_party/bitcoin/`](cpp/third_party/bitcoin/)
is **MIT** (Bitcoin Core developers, GPL-compatible), used unmodified with its
license/attribution preserved there.
## Credits
- Stratum V1; design lineage from [ckpool](https://bitbucket.org/ckolivas/ckpool).