Architektur
dvb-WarpPool ist ein Rust-Workspace mit 16 Library-Crates + 6 Binaries. Eine klare Schichtung — von puren Schema-Crates ohne async unten bis zu den async-orchestrierten Binaries oben — macht das System gut testbar und ermöglicht Sub-Crates separat zu betreiben (z.B. der Translator als sidecar ohne Pool-Daemon).
Crate-Übersicht
Schema + Foundation (ohne Async)
| Crate | Was |
|---|---|
warppool-profiles | Admin-Profile (Klein/Mittel/Gross/Enterprise) — capacity + defaults |
warppool-config | TOML-Schema (PoolConfig + Sub-Configs + Secrets) |
warppool-hwdetect | Hardware-Detection via sysinfo → Profile-Empfehlung |
Core Mining-Layer
| Crate | Was |
|---|---|
warppool-bitcoin-rpc | JSON-RPC + ZMQ-Subscribe für Bitcoin Core |
warppool-job-builder | Coinbase-Tx + Merkle-Tree + Stratum-Job-Konstruktion |
warppool-share-validator | Share-PoW-Check + Block-Found-Detection |
warppool-stratum-v1 | TCP-Listener, Session-State-Machine, VarDiff |
warppool-stratum-v2 | NOISE-NX-Handshake, binary-framing, Mining-Subprotocol |
warppool-translator | V1↔V2-Sidecar (Pool kann nur Sv2 anbieten, V1-Miner via Translator) |
Storage + API
| Crate | Was |
|---|---|
warppool-storage | SQLite via sqlx, alle Tabellen + Migrations |
warppool-api | Axum-HTTP-API (REST + SSE-Stream + statisches UI-serving) |
Operationale Subsysteme
| Crate | Was |
|---|---|
warppool-health | Bitcoin-Core-Multi-RPC-Health + bitcoin.conf-Parser + Snippet-Generator |
warppool-autoupdate | Version-Parser + GitHub-Release-Client + atomic_swap + Cosign-Hook |
warppool-notifier | Push-Sinks (ntfy/Telegram/Discord/Slack/Email) + Counter-Metrics |
warppool-telemetry | Vendor-API-Probes (AxeOS, NerdNOS, ...) + mDNS-Discovery + PoolMetrics |
Tools
| Crate | Was |
|---|---|
warppool-simulator | Sim-Miner (Vendor-Personas) + Sim-Node + Scenarios |
Binaries
| Binary | Zweck |
|---|---|
dvb-warppool-daemon | Der Pool — orchestriert alle Subsysteme |
dvb-warppool-cli | Operator-Tools (hash-password, token-create, set-profile, check-update, ...) |
dvb-warppool-setup | First-Run-Wizard (Axum, Modern-UI, embedded HTML) |
dvb-warppool-translator | V1→V2 Sidecar (clap-CLI, kann als systemd-service laufen) |
dvb-warppool-sim | Simulations-Runner (scenario list / run) |
Datenfluss (Happy Path)
+---------------------+
| Bitcoin Core |
| (mainnet / regtest) |
+---------------------+
| RPC | ZMQ pub
v v
+---------------------+
| bitcoin-rpc |
| - getblocktemplate |
| - submitblock |
| - hashblock watch |
+---------------------+
|
v
+---------------------+
| job-builder |
| - coinbase splits |
| - merkle tree |
| - StratumJob |
+---------------------+
|
+-----------+-----------+
v v
+------------------+ +------------------+
| stratum-v1 | | stratum-v2 |
| TCP / TCP+TLS | | TCP+NOISE-NX |
+------------------+ +------------------+
| |
v v
V1 Miner (Bitaxe, V2 Miner / Translator
NerdNOS, AntminerS23)
| |
v v
submit (extranonce, nonce, ntime)
|
v
+---------------------+
| share-validator |
| - PoW check |
| - block detection |
+---------------------+
|
+-----------+-----------+
v v
+------------------+ +------------------+
| storage | | block-submit-loop|
| - record_share | | - assemble block|
| - vardiff_state | | - submitblock |
+------------------+ +------------------+
|
v on success
+---------------------+
| notifier |
| fire BlockFound |
+---------------------+
| | | | |
v v v v v
ntfy tg disc slack email
Daemon-Task-Topologie
dvb-warppool-daemon ist ein Tokio-Multi-Reactor mit ~10 langlebigen Tasks
plus pro-Verbindung-Tasks. Alle Tasks teilen sich einen CancellationToken
für graceful shutdown.
+------- main() async -------+
| |
+- spawn ---------+----------+
| |
+-------+-------+ +------------------+
| Stratum-V1 | Stratum-V2 |
| accept-loop | accept-loop |
| (+ TLS) | (+ NOISE-NX) |
+----------------+ +-----------------+
| |
pro Connection: Session-Task
| |
v v
+---------------------------------+
| block_found_tx broadcast |
+---------------------------------+
|
+-------+--------+
| block_submit_ |
| loop |
+----------------+
|
Bitcoin Core RPC submitblock
|
+-------+--------+
| notifier.notify(BlockFound) |
+- spawn ---------+----------+
| job_refresh_loop |
| - poll getblocktemplate (60s) |
| - ZMQ hashblock fast-path |
| - drain & build job |
| - push to stratum handles |
+---------------------------------+
+- spawn ---------+----------+
| aggregate_loop (60s tick) |
| - storage.aggregate_5min |
| - SharesAccepted SSE event |
+---------------------------------+
+- spawn ---------+----------+
| health_check_loop (60s tick) |
| - check_bitcoin_health |
| - SSE HealthSnapshot |
| - RpcDown/Recovered notify |
+---------------------------------+
+- spawn ---------+----------+
| periodic_update_check (24h) |
| - GitHub Releases API |
| - SSE UpdateAvailable |
+---------------------------------+
+- spawn ---------+----------+
| miner_poll_loop (vendor probes) |
+---------------------------------+
+- spawn ---------+----------+
| HTTP API (Axum on :18334) |
| - REST endpoints |
| - SSE /api/events |
| - static UI from --ui-dir |
+---------------------------------+
Shared State
Tokio-Tasks teilen Arc<...>-Handles statt globaler Statics:
| Handle | Typ | Genutzt von |
|---|---|---|
notifier: Arc<Notifier> | shared via clone | block_submit_loop, health_check_loop, periodic_update_check, NotifierConnectionSink, API |
pool_metrics: Arc<PoolMetrics> | atomic counters | NotifierConnectionSink, BitcoinRpc, API /metrics-handler |
event_bus: Arc<PoolEventBus> | broadcast::Sender | alle Subsysteme — publish; API → subscribers (SSE) |
storage: Arc<Storage> | sqlx-Pool | share-recording, audit-log, vardiff-state, settings |
snapshot: Arc<RwLock<NetworkSnapshot>> | RwLock-Snapshot | job_refresh_loop schreibt; API liest |
profile_kind: Arc<RwLock<ProfileKind>> | hot-switchable | API admin-route + display |
cancel: CancellationToken | propagation | alle tasks (graceful shutdown) |
Storage-Schema
Tabellen aus den Phasen 1-15. Migrations in crates/storage/migrations/.
| Tabelle | Phase | Was |
|---|---|---|
workers | 1 | Worker-Liste (user, last_seen_at, shares_accepted/rejected, blocks_found) |
shares_raw | 1 | Letzte 1h roh-shares — basis für hashrate-Berechnung |
shares_5min | 1 | Aggregierte 5min-Buckets — Hashrate-Chart-Daten |
blocks_found | 1 | Block-History (height, hash, coinbase_value_sats, found_at) |
pool_settings | 2.5 | Generisches KV-Store (active_profile_kind, etc.) |
vardiff_state | 2.5 | Pro-Worker VarDiff-Snapshots (current_diff, ema, last_share_unix) |
audit_log | 3 | Admin-Actions (actor, action, target, peer_ip, ok, details) |
api_tokens | 3.2 | Persistente Bearer-Tokens (token_hash, name, scope, last_used_at, revoked_at) |
admin_2fa | 3.3 | TOTP-Secrets per User (secret_base32, enabled) |
push_subscriptions | 1 | Web-Push-Subscriptions (PWA, Phase B nicht aktiv) |
Sv2-Stack im Detail
Stratum V2 ist ein binary-framed protocol mit NOISE-NX-handshake davor. Im Pool-Kontext relevant sind zwei Subprotocols:
- Mining-Subprotocol (implementiert) — Channel-basiert, Extranonce-aware, Version-Rolling per BIP-320, Set-Target pro Channel.
- Template-Distribution-Protocol (TDP, Foundation in Phase 7.6a, Wiring
in 7.6b deferred) — Ersetzt
getblocktemplatedurch push-driven Template- Updates direkt vom Bitcoin-Node.
V1-Miner ----TCP plain----> stratum-v1 server ----------+
|
V1-Miner ----TCP+TLS------> stratum-v1 TLS-server -------+
|
v
+------------------+
| job-builder |
| share-validator |
+------------------+
^
|
V2-Miner ----TCP+NOISE----> stratum-v2 server (port 3334) +
|
V1-Miner ----TCP--+ |
| |
v |
dvb-warppool-translator (sidecar) --TCP+NOISE--------+
- SetupConnection
- OpenExtendedMiningChannel mit user_identity
- SubmitSharesExtended mit miner-extranonce
- Receives NewExtendedMiningJob + SetNewPrevHash
- Maps to V1 mining.notify + slushpool prev_hash + BIP-320 version-rolling
Konkretes Wire-Format ist in crates/stratum-v2/src/messages.rs mit
Roundtrip-Tests. NOISE-Handshake in crates/stratum-v2/src/noise.rs mit
snow-crate (pure-Rust, kein OpenSSL).
Connection-Lifecycle-Hooks
Beide Stratum-Server haben einen optionalen ConnectionSink-Trait. Bei
authentifizierten Workers (V1: mining.authorize, V2: OpenChannel mit user_identity) feuert on_authorized; bei Verbindungsende on_disconnect.
Daemon implementiert eine NotifierConnectionSink-Struct die beide Traits
implementiert. Pro-Worker-Debounce (default 30s) verhindert dass flappende
Miner pro Reconnect eine Notification feuern.
v1.Session::run
|
handle_authorize → connection_sink.on_authorized
|
... shares ...
|
loop exit → connection_sink.on_disconnect
v2.handle_connection
|
process_frame → channels().iter() → new user_identity?
| → on_authorized
... shares ...
|
loop exit → for each notified_user → on_disconnect
Event-Bus + SSE
PoolEventBus ist ein tokio-broadcast-channel mit PoolEvent-Variants:
| Event | Source |
|---|---|
BlockFound | block_submit_loop |
NewJob | job_refresh_loop |
SharesAccepted | aggregate_loop |
HealthSnapshot | health_check_loop (Phase 13b) |
UpdateAvailable | periodic_update_check (Phase 8e) |
API /api/events öffnet pro Client einen SSE-Stream mit dem aktuellen
Subscription-Set. UI nutzt das für HealthBanner + UpdateBanner + Toast-
Events.
Konfigurations-Layer
/etc/dvb-warppool/
├── config.toml # nicht-sensitive Settings
├── secrets.toml # chmod 600 — admin-hash, jwt-secret, rpc-pass, sv2-key
└── pool.db # SQLite (Path konfigurierbar)
Env-vars für opt-in subsysteme (siehe Configuration Reference):
WARPPOOL_HEALTH_CHECK_INTERVAL_SECONDS, WARPPOOL_AUTOUPDATE_REPO,
WARPPOOL_DISCONNECT_DEBOUNCE_SECS, sink-spezifische tokens, etc.
Lifecycle (Daemon-Boot)
- Parse CLI (clap) + ENV
- Load
config.toml+ (optional)secrets.toml - Validate config (
mining.payout_addresspflicht,ratelimitconstraints, ...) - Init tracing-subscriber (JSON wenn
logging.json) - Open SQLite + run migrations
- Resolve admin profile (persisted in
pool_settingsschlägt config-default) - Construct
BitcoinRpcmitwith_metrics(pool_metrics) - Probe RPC (warning bei Fail, Daemon startet trotzdem)
- Construct
Notifieraus config - Construct
PoolMetrics(Arc-shared) - Build initial job (oder skip wenn RPC fail)
- Spawn Stratum-V1-Listener (+ TLS-listener wenn cert/key configured)
- Spawn Stratum-V2-Listener wenn
sv2_listenconfigured +sv2_static_priv_key_hexda - Spawn job_refresh_loop (poll + optional ZMQ)
- Spawn block_submit_loop
- Spawn aggregate_loop (60s)
- Spawn health_check_loop wenn
WARPPOOL_HEALTH_CHECK_INTERVAL_SECONDS > 0 - Spawn periodic_update_check wenn
WARPPOOL_AUTOUPDATE_REPO+ interval > 0 - Spawn HTTP API (Axum auf
status_listen) - Install signal-handlers (SIGTERM → cancel.cancel() → alle tasks shutdown)
tokio::mainevent-loop läuft bis cancel
Crate-Abhängigkeiten (DAG)
Vereinfachtes Crate-Graph (nicht alle Edges; keine zyklischen Deps):
profiles --+
hwdetect --+--> config
|
+--> storage
+--> bitcoin-rpc --+
+--> job-builder |
+--> share-validator
+--> stratum-v1 -----+
+--> stratum-v2 -----+
+--> translator -----+
+--> telemetry -----+
+--> notifier ------+
+--> autoupdate ----+
+--> health --------+
+--> api -----------+
+--> simulator -----+
|
v
dvb-warppool-daemon
dvb-warppool-cli
dvb-warppool-setup
dvb-warppool-translator
dvb-warppool-sim
Crates und ihre einseitigen Deps:
telemetry← keine (foundation für metrics)bitcoin-rpc→telemetry(für RPC-Latency-Histogram, Phase 16.3)api→notifier+autoupdate+telemetry+storage+ ...daemon→ alle library-Crates plus runtime-deps (tokio, sqlx, ...)
Testing-Strategie
Drei Ebenen, plus eine zusätzliche operator-driven Ebene:
| Ebene | Was | Tool | Test-Count |
|---|---|---|---|
| Unit | Pure-logic pro Crate | cargo test -p warppool-<crate> | ~330 |
| Integration | Mehrere Crates in-process, axum-Mock-Server | tests/ pro Crate | ~115 |
| Sim | Gegen den echten Daemon-Prozess, Vendor-Personas | dvb-warppool-sim scenario <name> | 5 scenarios |
| Regtest E2E | Gegen echtes bitcoind im regtest | scripts/regtest-up.sh + --ignored | 3 tests (opt-in) |
Stand: 447 Tests + 3 ignored, alle grün. UI-side: pnpm svelte-check →
294 files, 0 errors.
Siehe TESTING.md für Details + Simulator-Workflow.