Blitzpool is an open-source Bitcoin mining pool with a single distinguishing feature: every payout — Solo, PPLNS, Group-Solo, Blockparty — is written directly into the coinbase transaction of the block that earned it. No pool wallet, no custody period, no FPPS-style intermediate. Your sats arrive at your address with the block itself.
This is the ground-up Rust rebuild of the original TypeScript Blitzpool — same non-custodial payout philosophy, re-architected around a multi-stream template pipeline, a self-tuning coinbase budget, and a Core/Satellite split that lets the pool scale horizontally. The protocol behaviour (Stratum V1/V2, coinbase distribution) is feature-equivalent with the TS pool; the internals are not.
License: GNU AGPL v3 (AGPL-3.0-or-later).
| Blitzpool | Typical FPPS / PPS+ | Custodial PPLNS | |
|---|---|---|---|
| Payouts go directly on-chain | ✅ same block as the find | ❌ batch cron, hours to days | ❌ threshold-based |
| Pool holds miner sats | ❌ never | ✅ between find & payout | ✅ until threshold |
| Minimum payout | none — it's just a coinbase output | typically 0.001 BTC+ | same |
| Stratum V1 | ✅ | ✅ | ✅ |
| Stratum V2 (Noise + TDP + JDP + extended channels) | ✅ actively developed | rare | almost never |
| Non-custodial Group-Solo (friends mine together, split on-chain) | ✅ | — | — |
| Non-custodial Blockparty (co-funded rentals, fixed-% on-chain split) | ✅ | — | — |
Every block mined on Blitzpool has the miner address(es) as the direct coinbase destination. An operator can't withhold payouts — they'd have to refuse to relay the block at all, and the miner could submit it elsewhere.
All four write their payout straight into the coinbase. They differ only in how the reward is split.
You versus Bitcoin. Your share wins → the entire coinbase goes to your address. No fee, no custody.
Sliding-window pooled mining with a multi-output coinbase and a signed credit/debit ledger. Every miner in the window gets their proportional cut as their own coinbase output.
- Window size:
window_factor × networkDifficulty(default4×) in diff-1-weighted shares — anti-hop by design (sliding window, not per-block reset). - A per-miner signed balance keeps the pool non-custodial to the sat: trimmed / sub-dust sats become a pending credit for the miner who earned them; the bonus recipient of the same block picks up a matching pending debit. Pool-wide balances sum to ~0 (bounded floor-rounding drift).
Friends mine together as a closed group; every block the group finds is split proportionally to each member's shares in that round, paid in the coinbase. Address-driven routing, admin-token auth, email-verified invitations. Per-block round reset is opt-in per group.
A directed group sharing a hashpower rental. Each member gets a fixed cut (basis points) of every block the rented hashrate finds, paid on-chain. Splits are agreed and signed off up front; trimmed/residual sats fold into the pool-fee output.
Blitzpool talks to bitcoin-core v31 over its Cap'n-Proto IPC socket (Template Distribution Protocol), not getblocktemplate + ZMQ. Each payout mode gets its own IPC client = its own template stream from core:
| Stream | Reservation | Serves |
|---|---|---|
| PPLNS (default) | autoscaled (see §2) | PPLNS connections |
| Solo | fixed | Solo connections |
| Group-Solo | fixed | Group-Solo connections |
| Blockparty | fixed (only when configured) | Blockparty rentals |
Per stream, the pool tells core exactly how much coinbase space to reserve via the IPC coinbase_output_max_additional_size field — derived from each mode's coinbase weight budget. There is no blockreservedweight to set in bitcoin.conf: the reservation is handed to core per-stream at runtime, and the streams are independent (only one stream's block ultimately wins, so reservations don't sum against a shared limit). A process only spawns the TDP streams when it holds the front role (see §4).
The PPLNS stream's coinbase weight budget self-tunes instead of being a fixed reservation. It steps between a floor ([pplns].coinbase_weight_budget, default 50 000 WU) and a hard ceiling ([coinbase_autoscale].max_weight_budget):
- Steps up when utilization ≥
up_threshold(default 0.85 — i.e. 15 % before it would trim) afterup_debouncesamples. - Steps down when utilization ≤
down_threshold(default 0.50) afterdown_debouncesamples. - Multiplicative
step_factor(default 1.15 = ±15 %), with acooldown_secsfloor between changes.
Effect: as the PPLNS window grows, the reservation grows ahead of it; when the window shrinks, the reservation shrinks and reclaims block space for fee-paying transactions. The alt streams (Solo / Group-Solo / Blockparty) stay on small fixed reservations sized to the largest group you expect.
The distribution builder reserves the structural coinbase overhead, then greedily keeps the largest payouts that fit the stream's budget and trims the smallest payouts to pending (carry-forward). Because the assembled coinbase can therefore never exceed the reservation core was told about, a found block is always valid — the pool can never build a coinbase that overflows its template and gets rejected by core.
Where a trimmed payout goes differs by mode, deliberately. PPLNS: the value stays inside the miners' cut as a signed pending credit and is repaid from a later block (WithheldValue::ToOtherMiners). Group-Solo: it falls into the §4 residual and is paid to the pool (WithheldValue::ToPool) — there is no group ledger and no carry-forward, which is why membership is hard-capped at what the coinbase can actually pay. Under-sizing a PPLNS budget therefore costs fairness (a small miner waits a block or two), never validity.
Capacity is monitored externally rather than by the pool: GET /api/pplns/groups/coinbase-capacity reports the member ceiling and GET /api/pplns/fees the PPLNS maxMinerOutputsAdaptive, each divided into the current fill (members.length from the group detail, or the length of GET /api/pplns/distribution).
The pool runs as role-gated processes that communicate over Redis streams:
front— Stratum listeners, template streams, job building, block submitapi— read-only HTTP APIpayout— ledger apply, confirmation gating, dust sweepstats— share-stats aggregationnotify— Telegram / ntfy / push / email fan-out
Roles are selected with --roles / BLITZPOOL_ROLES (independent of payout mode). Block-found and accepted/rejected shares flow Core→Satellite over Redis streams with exactly-once semantics; routing-cache invalidation is broadcast cross-process. See full-setup/DEPLOYMENT.md.
PPLNS and Group-Solo freeze a block's distribution and park it in Redis keyed by block hash; a confirmation watcher applies the ledger only after confirmation_depth (default 3) confirmations and discards orphaned candidates — no payout is ever booked for a block that didn't survive on-chain. Ledger applies are idempotent (history UNIQUE constraint + history-gated balance upsert), so stream redelivery or a duplicate block-found can't double-credit.
- Stratum V1 — full SV1 stack with vardiff,
mining.notify(ckpool-convention hex-padded fields), per-port difficulty. - Stratum V2 — Noise handshake, standard + extended channels (extranonce rolling, merkle reconstruction, pool-side share validation), Template Distribution Protocol, Job Declaration Protocol, SipHash-2-4. SV1 and SV2 share the same TCP ports; protocol detection on the first byte routes the socket.
| Role | Starting difficulty | Purpose |
|---|---|---|
| Default entry | configured (e.g. 1 000) | Solo / Group-Solo / Blockparty |
| High-diff rental | 1 000 000 | NiceHash / MRR / Braiins rentals — also the canonical Blockparty rental port |
| PPLNS opt-in | adaptive | Explicit opt-in to PPLNS payout |
| SV2 JDP | — | Job Declaration Protocol (when enabled) |
Routing priority on connect: explicit PPLNS port → Blockparty admin address → Group-Solo membership → Solo. Group-Solo and Blockparty are mutually exclusive per address. Extranonce2 size is 8 bytes (ample headroom for rental work distribution).
Configuration is TOML-first (parsed by bp-config), grouped into sections — the most relevant:
| Section / key | Purpose |
|---|---|
[tdp].socket_path |
bitcoin-core IPC socket for the template streams |
[pplns].coinbase_weight_budget |
PPLNS budget floor (default 50 000 WU); autoscaler grows from here |
[coinbase_autoscale] |
max_weight_budget (ceiling), up/down_threshold, step_factor, debounce, cooldown |
[group_fees].coinbase_weight_budget |
Group-Solo + Blockparty fixed budget (default 10 000 WU ≈ 50 members) |
[group_fees].address / .percent |
Shared Group-Solo/Blockparty fee lane (falls back to [pplns] fee) |
[solo].coinbase_weight_budget / [blockparty].coinbase_weight_budget |
Per-mode fixed alt-stream reservations |
--roles / BLITZPOOL_ROLES |
Deployment topology override (front/api/payout/stats/notify) |
Schema is applied via sqlx migrations run at boot (advisory-locked + idempotent, so every process in a split can run them safely). Postgres + Redis are required — there is no SQLite path. Upstream SV2-stack dependency pins and bump strategy live in UPSTREAM_DEPS.md.
The HTTP API (served by the api role) mirrors the TS pool's surface — pool-wide (/api/info/*, /api/network), per-address (/api/client/:address/*, /api/pplns/mode/:address), PPLNS (/api/pplns/*), Group-Solo (/api/pplns/groups/*), Blockparty (/api/blockparty/*), and email-binding endpoints. Rust-build additions include:
GET /api/pplns/groups/coinbase-capacity— worst-case member ceiling for the Group-Solo coinbase budget (UI shows used / free slots per group).GET /api/pplns/groups/finder-bonus-cap— current block subsidy, used to cap the finder-bonus input.
cargo build --release # builds the `blitzpool` binary
cargo test --workspace # unit + integration tests (~40% of the tree is tests)
cargo clippy --workspace # lintsRegtest-driven integration tests (TDP/IPC + RPC paths) spin up bitcoin-core via the in-tree bp-regtest-harness; the bp-template-distribution and bp-job-declaration test suites are the bitcoin-core compatibility canaries. A regtest deploy helper lives at build_and_deploy_regtest.sh, and the split-validation runbook at full-setup/REGTEST-SPLIT-VALIDATION.md.
Rust workspace of 37 crates (bp-* + the blitzpool binary). Async on tokio; HTTP on axum; DB on sqlx (Postgres); Redis for the share/cache/stream bus. SV2 protocol primitives from the SRI stratum-core; bitcoin-core IPC bridge from sv2-apps (bitcoin_core_sv2) — see UPSTREAM_DEPS.md.
The frontend lives in its own repo: blitzpool-ui (Angular). It talks to the api role's HTTP endpoints and exposes the per-miner dashboard, payout-group + Blockparty admin flows, public group directory, and a mining-modes explainer.
Built by the Blitzpool team at yourdevice.ch. The non-custodial coinbase-payout design originated in the TypeScript Blitzpool (itself a fork of public-pool); this repository is the independent Rust reimplementation.
Made in Switzerland. 🇨🇭
by Bitcoiners, for Bitcoiners who verify instead of trust.