Stratum V1 · Stratum V1 over TLS · native Stratum V2 (Noise-encrypted) · 9 coin families · one codebase
Live pool · Benchmark report · Quick start · Contributing · Security policy
English | 简体中文 | Русский | Español | Português (Brasil) | Deutsch | Français | 日本語 | 한국어 | Türkçe
mkpool is a high-performance, multi-threaded solo mining pool engine. It speaks Stratum V1, Stratum V1 over TLS, and native Stratum V2 (Noise-encrypted), and runs nine coin families (including merge-mined Dogecoin and Equihash Zcash) out of a single codebase. It is live on mainnet today, powering mkpool.com; this README reflects the deployed state.
On identical hardware, mkpool delivers roughly 2.8x the share throughput, 3.2x lower median latency, and 16x the reconnect capacity of ckpool, measured by a fully reproducible open source benchmark (details below).
- Why mkpool?
- Benchmarks: mkpool vs ckpool
- Supported coins
- Feature comparison: mkpool vs ckpool
- Quick start
- Testing and hardening
- Architecture
- Recent changes
- Project scope
- Contributing
- Support the project
- Acknowledgments
- Attribution and license
- ⚡ Fast where it matters. ~330k fully validated shares per second on an 8-core box, sub-millisecond submit-to-ack at every percentile, and reconnect storms (NiceHash, MiningRigRentals style) absorbed at ~6,400 full connect cycles per second.
- 🔐 Encrypted Stratum, in the binary. TLS (
stratum+ssl://) and native Stratum V2 with a NoiseNXhandshake and signed authority certificates. No stunnel, no external proxy. - 🪙 Nine coin families, one codebase. BTC, BCH, BC2, BCH2, XEC, DGB, LTC with merge-mined DOGE (AuxPoW), and Equihash ZEC, each a config file away.
- 🎯 True solo mining. The miner's username is their payout address; the coinbase is rebuilt per session so block rewards go straight to the finder's wallet.
- 🛡️ Hardened against hostile traffic. Token-bucket rate limiting, auto-ban on invalid-share floods, in-memory blacklist, stale-by-block and duplicate-share rejection, and a published fuzzing harness that beats on the Stratum parser.
- 🧪 Engineered like software, not folklore. Unit tests, ASan/TSan/UBSan sweeps, CI-friendly CMake + Ninja builds, and Prometheus metrics built in.
- 🏭 Production-proven. Every feature in this repo runs on mainnet right now, across all nine chains, under real rented-hashrate churn.
A fair, fully reproducible Stratum benchmark on two identical 8-core boxes (Azure Standard_D8lds_v7), one pool at a time, same bitcoind regtest node, same load generator, fixed difficulty 1. Every submitted share is fully validated (coinbase rebuild, merkle root, 80-byte header, double SHA-256) before the pool answers, and the reject reasons prove it on both sides.
| Scenario | mkpool | ckpool | Margin |
|---|---|---|---|
| Sustained validated shares/s (128 to 2,048 connections) | ~315k to 337k | ~108k to 118k | ~2.8x |
| Median submit-to-ack latency (100 connections, light load) | 116 µs | 371 µs | ~3.2x lower |
| 99th percentile latency | 602 µs | 814 µs | lower at every percentile |
| Reconnect cycles/s (200 parallel connect-subscribe-authorize-submit-close loops) | ~6,391 (4 errors) | ~402 (1,000+ errors) | ~16x |
| Resident memory at 2k / 4k / 8k idle connections | 66 / 108 / 197 MiB | 25 / 39 / 68 MiB | ckpool ~2.7x leaner |
ckpool's memory win is published exactly as measured: its tight C footprint is a genuine engineering achievement, and the trade-off of mkpool's heavier per-connection buffering and threading model is real. Everything else went to mkpool, and the ratios barely move as load rises.
- 📊 Full write-up with methodology and charts
- 📄 The exact self-contained HTML report
- 🔁 Reproduce it yourself: benchmark kit (load generator, orchestrator, configs)
Tip when benchmarking mkpool yourself: use a real, valid payout address as the Stratum username. mkpool validates addresses locally at authorize time and early-rejects invalid usernames, which would unfairly skip the work being measured.
| Coin | Ticker | Algorithm | Notes |
|---|---|---|---|
| Bitcoin | BTC | SHA-256d | V1, TLS, SV2 |
| Bitcoin Cash | BCH | SHA-256d | CashAddr, V1/TLS/SV2 |
| BitcoinII | BC2 | SHA-256d | V1/TLS/SV2 |
| Bitcoin Cash II | BCH2 | SHA-256d | CashAddr, V1/TLS/SV2 |
| eCash | XEC | SHA-256d | Avalanche pre-consensus, SV2 |
| DigiByte | DGB | SHA-256d | V1/TLS/SV2 |
| Litecoin | LTC | Scrypt | merge-mines DOGE |
| Dogecoin | DOGE | Scrypt (AuxPoW) | merge-mined on LTC |
| Zcash | ZEC | Equihash 200,9 | mining.set_target, Blossom subsidy |
Legend: ✅ supported ·
| Capability | mkpool | ckpool |
|---|---|---|
Stratum V1 (mining.*) |
✅ | ✅ |
Stratum V1 over TLS (stratum+ssl://) |
✅ in-binary any_stream variant, SIGHUP cert reload |
❌ |
Stratum V2 native (Noise NX handshake, encrypted) |
✅ full-block mode, collects fees | ❌ |
| SV2 secret-authority key / signed certs | ✅ | ❌ |
SV2 empty-block vs full-block toggle (v2EmptyBlocks) |
✅ | ❌ |
BIP310 mining.configure (version-rolling negotiation) |
✅ | ✅ |
ASICBoost / version-mask (version_mask) |
✅ validated (BIP310) | ✅ |
subscribe-extranonce extension |
✅ | ✅ |
Suggested difficulty (mining.suggest_difficulty, d= in password) |
✅ clamped per coin | ✅ |
| Capability | mkpool | ckpool |
|---|---|---|
| Bitcoin (BTC, SHA-256d) | ✅ | ✅ |
| Bitcoin Cash (BCH, SHA-256d, CashAddr) | ✅ | ❌ |
| BitcoinII (BC2, SHA-256d) | ✅ | ❌ |
| Bitcoin Cash II (BCH2, SHA-256d, CashAddr) | ✅ | ❌ |
| eCash (XEC, SHA-256d + Avalanche pre-consensus) | ✅ | ❌ |
| DigiByte (DGB, SHA-256d) | ✅ | ❌ |
| Litecoin (LTC, Scrypt) | ✅ | ❌ |
| Dogecoin merge-mined on LTC (AuxPoW) | ✅ parent + aux blocks | ❌ |
Zcash (ZEC, Equihash 200,9, mining.set_target) |
✅ | ❌ |
| Single codebase, per-coin config | ✅ 9 families | ❌ Bitcoin-only |
| Equihash share validation (in-process) | ✅ equihash.hpp + unit test |
❌ |
| Blossom-aware subsidy / halving (ZEC) | ✅ | ❌ |
| Capability | mkpool | ckpool |
|---|---|---|
| Language / standard | C++23 | C |
| Concurrency model | Single-process, async io_context worker pool (std::jthread) |
Multi-process (fork) + threads, Unix-socket IPC |
| Networking | Boost.Asio / Beast, per-session strand | Hand-coded epoll + Unix sockets |
| Session map | Sharded (default 64 shards), low-contention broadcast | Hash tables (uthash) |
| Per-session write path | Strand-bound WriteQueue + 1 MiB watermark (no async_write races) |
epoll-driven send buffers |
| Job/work window | JobWindow rolling buffer (default 32 jobs) keyed by job_id |
Workbase list |
| New work on block change | ✅ full tx set, ZMQ-driven, no transactionless work | ✅ |
| Periodic job rebroadcast (keepalive for strict clients) | ✅ 30s, resets on real blocks | ✅ |
| ZMQ block-hash notification | ✅ edge-trigger bug fixed | ✅ (optional) |
bitcoind failover (multiple nodes) |
✅ | ✅ |
| Solo coinbase (miner address = username) | ✅ per-session coinbase2 rebuild | ✅ (BTCSOLO mode) |
| Operator fee / donation from coinbase | ✅ configurable %, incl. aux/DOGE split | ✅ default 0.5% |
| Custom coinbase signature | ✅ configurable | ✅ configurable |
| Proxy mode | ❌ | ✅ |
| Passthrough / node / redirector modes | ❌ | ✅ |
| Seamless restart via socket handover | ❌ (systemd restart per coin) | ✅ |
| Capability | mkpool | ckpool |
|---|---|---|
| Vardiff (EMA / decaying-average) | ✅ faithful re-impl of ckpool decay_time/time_bias |
✅ (original) |
| Per-coin vardiff ranges | ✅ (e.g. BTC/BCH/BC2/BCH2/DGB/XEC [1024, 1M], ZEC [8192, 524288]) |
mindiff/maxdiff |
| Fixed-difficulty tiers (one TCP port each) | ✅ e.g. 10M / 50M / 100M ports | |
Custom d= clamp (1024-10M) |
✅ | |
| Stale-by-block share rejection | ✅ prevhash checked against current tip | ✅ |
| Duplicate-share rejection | ✅ in-memory dedupe set, cleared per block | ✅ |
| ntime validation (BIP113-compatible) | ✅ utils::valid_ntime |
✅ |
int64_t coinbase value (overflow-safe) |
✅ end-to-end | ✅ |
| Local address validation (no RPC per authorize) | ✅ BIP173/BIP350/base58/CashAddr decoders |
| Capability | mkpool | ckpool |
|---|---|---|
| Token-bucket per-IP rate limiting | ✅ | |
| Auto-ban on excessive invalid shares | ✅ | |
| In-memory IP blacklist | ✅ | |
fail2ban + nftables integration (per-IP conn cap) |
✅ documented | ❌ |
| Connection-drop observability (per-disconnect logs) | ✅ reason/worker/lifetime/shares | |
| Database resilience (auto-reconnect + zero-loss requeue) | ✅ | n/a (no DB) |
| Prometheus metrics endpoint | ✅ optional (MKPOOL_ENABLE_METRICS) |
❌ |
| Sanitizer builds (ASan / TSan / UBSan) | ✅ CMake options + scripts/run_sanitizers.sh |
❌ |
| Unit tests (Catch2 / Catch-style) | ✅ merkle, vardiff, address, SV2 noise, etc. | ❌ |
| Stratum fuzzing harness | ✅ scripts/fuzz_*.sh (7 abuse categories, daemon-survival assertions) |
❌ |
| Build system | CMake + Ninja | autotools (./configure && make) |
| Platform | Linux (Ubuntu 24.04+) | Linux |
| External dependencies | Boost, OpenSSL, libpq/pqxx, libzmq, libsodium | Minimal (glibc, yasm, optional zmq) |
Where ckpool still wins: ckpool's proxy / passthrough / redirector / node topology lets it fan a single upstream pool out to millions of clients with near-zero per-client overhead, its socket-handover restart is genuinely seamless, and it holds idle connections in roughly a third of mkpool's RAM. mkpool is a single-process solo-pool engine and does not implement those scale-out roles (yet). It trades that for raw throughput, encryption, multi-coin support, and modern memory-safety tooling ckpool does not have.
# system deps
sudo apt update
sudo apt install -y build-essential cmake ninja-build pkg-config git \
libboost-system-dev libboost-thread-dev libboost-program-options-dev \
libssl-dev libpq-dev libpqxx-dev libzmq3-dev cppzmq-dev libsodium-dev libsecp256k1-dev
# clone + configure + build (C++23)
git clone https://github.com/Mecanik/mkpool.git && cd mkpool
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build -jCMake options
| Option | Default | Description |
|---|---|---|
MKPOOL_BUILD_TESTS |
ON |
Catch2 unit tests |
MKPOOL_ENABLE_LTO |
ON |
Link-time optimization |
MKPOOL_ENABLE_TLS |
ON |
OpenSSL TLS context support |
MKPOOL_ENABLE_METRICS |
ON |
Prometheus exposer |
MKPOOL_ENABLE_ASAN |
OFF |
AddressSanitizer |
MKPOOL_ENABLE_TSAN |
OFF |
ThreadSanitizer |
MKPOOL_ENABLE_UBSAN |
OFF |
UndefinedBehaviorSanitizer |
MKPOOL_ENABLE_NATIVE |
OFF |
-march=native |
You need a synced coin node (with ZMQ block notifications enabled) and a reachable PostgreSQL instance.
cp config.json.example config.json
# then edit config.json:
# - RPC host/credentials for your coin daemon(s)
# - PostgreSQL credentials
# - YOUR donation/payout addresses (never keep the placeholders)
# - stratum tiers/ports, vardiff ranges, optional TLS cert paths, optional SV2 portconfig.json.example documents every field the loader understands, including fixed-difficulty tiers, TLS tiers ("tls": true), Stratum V2 settings, and LTC+DOGE merge mining (aux block).
./build/mkpool --config config.jsonA running pool exposes, depending on config:
- Stratum V1: vardiff and fixed-difficulty tiers, one port each (e.g.
3331vardiff,3335fixed-10M). - Stratum over TLS: any tier with
"tls": truespeaksstratum+ssl://on its port. - Stratum V2 (Noise): the
stratumV2Port(e.g. BTC3340). - Prometheus metrics:
metricsListenPort(default9090) when built with metrics.
Miners connect with their payout address as username; the block reward goes straight to that address.
cd build
ctest --output-on-failure -jscripts/run_sanitizers.sh builds the unit tests under AddressSanitizer, UndefinedBehaviorSanitizer, and ThreadSanitizer in a throwaway .san/ directory (your normal build/ is left untouched) and reports any findings.
./scripts/run_sanitizers.sh # asan+ubsan and tsan
./scripts/run_sanitizers.sh asan # a single flavor
./scripts/run_sanitizers.sh --fuzz # also fuzz a sanitized instancescripts/fuzz_*.sh throw malformed and abusive Stratum traffic at a running pool and assert it survives (same PID before and after) with no handler exceptions. Point them at a local instance:
# quick malformed-frame battery
HOST=127.0.0.1 PORT=3331 ./scripts/fuzz_stratum.sh
# full suite: malformed JSON, protocol abuse, share spam, auth abuse,
# slowloris, version-rolling abuse, binary noise
HOST=127.0.0.1 PORT=3331 ./scripts/fuzz_suite.shflowchart LR
N["bitcoind / node<br/>(RPC + ZMQ)"] -- "ZMQ hashblock + GBT" --> G[Generator]
G -- JobPtr --> S[Stratifier]
S --> PM["PoolManager<br/>(sharded session map)"]
PM -- "notifyNewJob<br/>(per-session strand)" --> CS["ClientSession × N<br/>V1 / TLS / SV2 Noise"]
RL[RateLimiter] --- CS
CS -- "accepted shares" --> DB["DB worker<br/>(PostgreSQL)"]
M[Miners] <--> CS
IoPoolruns N workerio_contexts (default =hardware_concurrency()).- Each
ClientSessionlives on one workerio_contextvia an Asio strand; the socket type (plain / TLS / SV2 Noise) is abstracted behindany_stream, and all writes go through a strand-boundWriteQueue. PoolManageriterates shards on eachJobPtrand dispatchesnotifyNewJobto every session's strand.- The
Generatorrebroadcasts the current job every 30 seconds as a keepalive (withclean_jobs=false, so no work is discarded), which keeps strict clients such as rental-marketplace proxies and farm controllers from idle-disconnecting between blocks.
- 2026-07-01: Stratum V2
SetupConnection.Successflags corrected to spec (request and response flags are different namespaces). - 2026-06-29: In-process miner-id cache keeps authorize/reconnect storms off the database; write queue made strand-bound and teardown-safe under heavy connection churn; Nagle disabled on accepted sockets.
- 2026-06-28: Stale-by-block and duplicate-share rejection with an in-memory dedupe table, preventing effort double-counting from replayed or in-flight shares across block changes.
- 2026-06-15: Periodic 30-second job keepalive rebroadcast so strict miners and rental proxies never idle-time-out.
- 2026-06-13: BitcoinII (BC2) and Bitcoin Cash II (BCH2) support.
This repository is the pool engine, published for transparency. The operational stack that surrounds it in production (the database/analytics service, the public REST API, and the website) is not part of this open release.
mkpool is an original codebase. The async C++ engine, multi-coin support, Stratum V2 (Noise) and TLS stack, per-miner solo coinbase construction, and security tooling were all written from scratch. The one component that intentionally borrows from ckpool (Con Kolivas' GPLv3 C pool) is the variable-difficulty retarget math, a small, attributed re-implementation of a well-proven algorithm (see Attribution and license).
Contributions are welcome: bug reports, protocol edge cases, new coin families, performance work, documentation, and translations of this README.
- Read CONTRIBUTING.md before opening a PR.
- Security issues: please follow SECURITY.md instead of opening a public issue.
- If mkpool is useful to you, starring the repo genuinely helps the project get found. ⭐
mkpool is free and open source. There is no fee to use the code and no built-in donation skim. If the project has been useful to you and you would like to chip in toward its development, you can send a tip here. It is entirely optional and very much appreciated.
BTC: bc1qlugz6as6x3n03c6x8zddpnmypsaucdmh3lc5z0
mkpool is built on top of a lot of excellent open source work. A sincere thank you to the maintainers and contributors of every project below. The pool would not exist without them.
| Library | License | Used for |
|---|---|---|
| Boost (Asio / Beast) | BSL-1.0 | Async networking, strands, HTTP RPC client |
| OpenSSL | Apache-2.0 | TLS, SHA-256 |
| fmt | MIT | Hot-path Stratum formatting |
| spdlog | MIT | Logging |
| nlohmann/json | MIT | Config and RPC JSON |
| cxxopts | MIT | Command-line parsing |
| libpqxx / libpq | BSD-3-Clause / PostgreSQL | Database access |
| ZeroMQ (libzmq + cppzmq binding) | MPL-2.0 / MIT | Block-hash notifications |
| libsodium | ISC | Stratum V2 Noise crypto |
| libsecp256k1 | MIT | EC keys / signatures (SV2) |
| Catch2 | BSL-1.0 | Unit tests |
| prometheus-cpp | MIT | Optional metrics endpoint |
All of these are under GPLv3-compatible licenses. mkpool does not vendor (copy) their source; they are linked from your system package manager or fetched by CMake at build time. If you distribute a compiled mkpool binary, ship a THIRD-PARTY-NOTICES file reproducing these projects' copyright and license texts alongside it.
mkpool is original software, © 2025-2026 Mecanik1337 (contact@mecanik.dev), licensed under the GNU General Public License v3.0 (GPL-3.0). Every source file carries the full GPLv3 header.
Almost all of the codebase (the async engine, multi-coin support, Stratum V2 (Noise) and TLS, solo coinbase construction, and security tooling) is written from scratch and owes nothing to ckpool beyond being the same kind of program.
The single exception, disclosed for honesty and license compliance: the variable-difficulty retarget math in vardiff.cpp / vardiff.hpp re-implements ckpool's decay_time() (src/libckpool.c) and time_bias() / add_submit() (src/stratifier.c) by Con Kolivas (also GPLv3). That is the only part adapted from ckpool; no ckpool C source files are vendored or copied verbatim, and a few Stratum field conventions (e.g. 4-byte extranonce1) simply follow common practice. These are attributed inline. Because mkpool is GPLv3, this re-use is fully permitted; if you redistribute mkpool, keep it under GPLv3, retain these attributions, and ship the full license text (COPYING).
ckpool: https://bitbucket.org/ckolivas/ckpool, © 2014-2026 Con Kolivas.
If you run mkpool, find a block with it, or just like the engineering, a star is the easiest way to support the project.