Skip to content

Mecanik/mkpool

Repository files navigation

mkpool

Modern multi-coin solo mining pool engine, written in C++23

Stratum V1 · Stratum V1 over TLS · native Stratum V2 (Noise-encrypted) · 9 coin families · one codebase

License: GPLv3 C++23 Platform: Linux Stratum Benchmark GitHub stars

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).

Table of contents

Why mkpool?

  • 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 Noise NX handshake 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.

Benchmarks: mkpool vs ckpool

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.

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.

Supported coins

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

Feature comparison: mkpool vs ckpool

Legend: ✅ supported · ⚠️ partial / conditional · ❌ not supported

Protocols and encryption

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

Coins, algorithms and merge mining

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)

Pool engine and architecture

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)

Difficulty and share handling

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]) ⚠️ single mindiff/maxdiff
Fixed-difficulty tiers (one TCP port each) ✅ e.g. 10M / 50M / 100M ports ⚠️ via separate instances
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 ⚠️ relies on bitcoind

Security and operations

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.

Quick start

1. Build (Ubuntu 24.04+)

# 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 -j
CMake 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

2. Configure

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 port

config.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).

3. Run

./build/mkpool --config config.json

A running pool exposes, depending on config:

  • Stratum V1: vardiff and fixed-difficulty tiers, one port each (e.g. 3331 vardiff, 3335 fixed-10M).
  • Stratum over TLS: any tier with "tls": true speaks stratum+ssl:// on its port.
  • Stratum V2 (Noise): the stratumV2Port (e.g. BTC 3340).
  • Prometheus metrics: metricsListenPort (default 9090) when built with metrics.

Miners connect with their payout address as username; the block reward goes straight to that address.

Testing and hardening

Unit tests

cd build
ctest --output-on-failure -j

Sanitizer sweeps

scripts/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 instance

Fuzz the Stratum parser

scripts/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.sh

Architecture

flowchart 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
Loading
  • IoPool runs N worker io_contexts (default = hardware_concurrency()).
  • Each ClientSession lives on one worker io_context via an Asio strand; the socket type (plain / TLS / SV2 Noise) is abstracted behind any_stream, and all writes go through a strand-bound WriteQueue.
  • PoolManager iterates shards on each JobPtr and dispatches notifyNewJob to every session's strand.
  • The Generator rebroadcasts the current job every 30 seconds as a keepalive (with clean_jobs=false, so no work is discarded), which keeps strict clients such as rental-marketplace proxies and farm controllers from idle-disconnecting between blocks.

Recent changes

  • 2026-07-01: Stratum V2 SetupConnection.Success flags 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.

Project scope

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).

Contributing

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. ⭐

Support the project

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

Acknowledgments

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.

Attribution and license

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.


⬆ back to top

If you run mkpool, find a block with it, or just like the engineering, a star is the easiest way to support the project.

Releases

No releases published

Sponsor this project

 

Packages

 
 
 

Contributors