ix ckpool.conf render dropping the TLS bind port; Let deployments declare what each stratum bind is
This commit is contained in:
@@ -7,6 +7,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -65,6 +66,18 @@ func main() {
|
||||
agg := state.New(ck, rpc, cfg.PollInterval, log)
|
||||
agg.Store = blockStore
|
||||
agg.MempoolBaseURL = cfg.MempoolBaseURL
|
||||
// Purely descriptive (it drives the dashboard's connection badge), so a
|
||||
// malformed value degrades to the UI's fallback rather than refusing to
|
||||
// start a pool that is otherwise fine.
|
||||
if cfg.StratumServersJSON != "" {
|
||||
var servers []state.StratumServer
|
||||
if err := json.Unmarshal([]byte(cfg.StratumServersJSON), &servers); err != nil {
|
||||
log.Warn("ignoring malformed STRATUM_SERVERS", "err", err)
|
||||
} else {
|
||||
agg.StratumServers = servers
|
||||
log.Info("stratum binds declared", "count", len(servers))
|
||||
}
|
||||
}
|
||||
agg.LogFilePath = cfg.CKPoolLogFile
|
||||
agg.KillCKPool = killCKPool(log)
|
||||
|
||||
|
||||
@@ -39,21 +39,29 @@ type Config struct {
|
||||
// the UI uses the public mempool.space; non-empty means the user
|
||||
// has pointed Kamado at their own instance via the StartOS config.
|
||||
MempoolBaseURL string
|
||||
|
||||
// Optional JSON array describing ckpool's serverurl[] binds, so the
|
||||
// dashboard can name the transport a miner connected over instead of
|
||||
// guessing from the bind index. Rendered by whoever writes
|
||||
// ckpool.conf; empty means "undeclared" and the UI falls back. Parsed
|
||||
// in main (the concrete type lives with the snapshot it belongs to).
|
||||
StratumServersJSON string
|
||||
}
|
||||
|
||||
func FromEnv() (*Config, error) {
|
||||
cfg := &Config{
|
||||
ListenAddr: getenv("LISTEN_ADDR", ":8080"),
|
||||
CKPoolSockDir: getenv("CKPOOL_SOCKDIR", "/run/ckpool"),
|
||||
BitcoinRPCURL: os.Getenv("BITCOIN_RPC_URL"),
|
||||
BitcoinRPCUser: os.Getenv("BITCOIN_RPC_USER"),
|
||||
ListenAddr: getenv("LISTEN_ADDR", ":8080"),
|
||||
CKPoolSockDir: getenv("CKPOOL_SOCKDIR", "/run/ckpool"),
|
||||
BitcoinRPCURL: os.Getenv("BITCOIN_RPC_URL"),
|
||||
BitcoinRPCUser: os.Getenv("BITCOIN_RPC_USER"),
|
||||
BitcoinRPCPassword: os.Getenv("BITCOIN_RPC_PASSWORD"),
|
||||
BitcoinRPCTimeout: getenvDuration("BITCOIN_RPC_TIMEOUT", 10*time.Second),
|
||||
BitcoinZMQBlock: os.Getenv("BITCOIN_ZMQ_BLOCK"),
|
||||
CKPoolLogFile: getenv("CKPOOL_LOGFILE", "/var/log/ckpool/ckpool.log"),
|
||||
DBPath: getenv("DB_PATH", "/var/lib/kamado/kamado.db"),
|
||||
PollInterval: getenvDuration("POLL_INTERVAL", 5*time.Second),
|
||||
MempoolBaseURL: os.Getenv("MEMPOOL_BASE_URL"),
|
||||
BitcoinRPCTimeout: getenvDuration("BITCOIN_RPC_TIMEOUT", 10*time.Second),
|
||||
BitcoinZMQBlock: os.Getenv("BITCOIN_ZMQ_BLOCK"),
|
||||
CKPoolLogFile: getenv("CKPOOL_LOGFILE", "/var/log/ckpool/ckpool.log"),
|
||||
DBPath: getenv("DB_PATH", "/var/lib/kamado/kamado.db"),
|
||||
PollInterval: getenvDuration("POLL_INTERVAL", 5*time.Second),
|
||||
MempoolBaseURL: os.Getenv("MEMPOOL_BASE_URL"),
|
||||
StratumServersJSON: os.Getenv("STRATUM_SERVERS"),
|
||||
}
|
||||
|
||||
if cfg.BitcoinRPCURL == "" {
|
||||
|
||||
@@ -25,6 +25,21 @@ type HashratePoint struct {
|
||||
V float64 `json:"v"` // H/s
|
||||
}
|
||||
|
||||
// StratumServer describes one entry in ckpool's serverurl[] array. ckpool
|
||||
// tags every client with the index of the bind it arrived on, but the index
|
||||
// alone doesn't say what that bind *is* — that depends on how the deployment
|
||||
// rendered ckpool.conf. Declaring the array lets the dashboard report the
|
||||
// actual transport (plaintext, TLS with which certificate) instead of
|
||||
// hardcoding a bind order.
|
||||
type StratumServer struct {
|
||||
// Kind is the stable machine-readable tag the UI switches on:
|
||||
// "plain", "tls-local" (package-managed self-signed certificate) or
|
||||
// "tls-public" (CA-issued certificate for a public domain).
|
||||
Kind string `json:"kind"`
|
||||
// Label is the human-readable description shown on hover.
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
// Snapshot is the merged view served to the UI. All fields are safe to
|
||||
// JSON-serialize directly.
|
||||
type Snapshot struct {
|
||||
@@ -38,7 +53,7 @@ type Snapshot struct {
|
||||
Clients []ckpool.StratumClient `json:"clients"`
|
||||
|
||||
// Derived/enriched fields
|
||||
HashrateHs float64 `json:"hashrate_hs_1m"` // from PoolStats.DSPS1
|
||||
HashrateHs float64 `json:"hashrate_hs_1m"` // from PoolStats.DSPS1
|
||||
HashrateHs5m float64 `json:"hashrate_hs_5m"`
|
||||
HashrateHs1h float64 `json:"hashrate_hs_1h"`
|
||||
HashrateHs24h float64 `json:"hashrate_hs_24h"`
|
||||
@@ -68,8 +83,8 @@ type Snapshot struct {
|
||||
NextDifficultyPercent float64 `json:"next_difficulty_percent"`
|
||||
|
||||
// Bitcoin Core fields
|
||||
Chain *bitcoind.BlockchainInfo `json:"chain"`
|
||||
NetworkHashrateHs float64 `json:"network_hashrate_hs"`
|
||||
Chain *bitcoind.BlockchainInfo `json:"chain"`
|
||||
NetworkHashrateHs float64 `json:"network_hashrate_hs"`
|
||||
|
||||
// Recent found blocks (in-memory history; persisted in Phase 2b.5).
|
||||
RecentBlocks []BlockRecord `json:"recent_blocks,omitempty"`
|
||||
@@ -82,6 +97,13 @@ type Snapshot struct {
|
||||
// the StartOS config "Block Explorer" -> "Custom URL".
|
||||
MempoolBaseURL string `json:"mempool_base_url,omitempty"`
|
||||
|
||||
// Describes ckpool's serverurl[] array: entry N tells the UI what a
|
||||
// client with `server == N` is actually connected over. Set via the
|
||||
// STRATUM_SERVERS env by whoever renders ckpool.conf (the StartOS
|
||||
// wrapper). Empty when undeclared, in which case the UI falls back to
|
||||
// its historical "index 1 means TLS" assumption.
|
||||
StratumServers []StratumServer `json:"stratum_servers,omitempty"`
|
||||
|
||||
// Counts of share-submit attempts ("Possible/Submitting block solve"
|
||||
// log lines) and confirmed solves ("Solved and confirmed block").
|
||||
// A growing gap means bitcoind is rejecting our submissions or
|
||||
@@ -93,15 +115,15 @@ type Snapshot struct {
|
||||
// LastZMQEventAge is the seconds-since the last bitcoind hashblock
|
||||
// frame arrived; -1 means no event seen since startup. ZMQEnabled
|
||||
// is whether the user configured an endpoint at all.
|
||||
ZMQEnabled bool `json:"zmq_enabled"`
|
||||
LastZMQEventAge float64 `json:"last_zmq_event_age,omitempty"` // seconds; >=0
|
||||
HasLastZMQEvent bool `json:"has_last_zmq_event"`
|
||||
TipChangedAge float64 `json:"tip_changed_age"` // seconds since tip height last changed
|
||||
ZMQEnabled bool `json:"zmq_enabled"`
|
||||
LastZMQEventAge float64 `json:"last_zmq_event_age,omitempty"` // seconds; >=0
|
||||
HasLastZMQEvent bool `json:"has_last_zmq_event"`
|
||||
TipChangedAge float64 `json:"tip_changed_age"` // seconds since tip height last changed
|
||||
|
||||
// Bitcoin Core peer connections.
|
||||
PeerCount int `json:"peer_count"`
|
||||
PeersIn int `json:"peers_in"`
|
||||
PeersOut int `json:"peers_out"`
|
||||
PeerCount int `json:"peer_count"`
|
||||
PeersIn int `json:"peers_in"`
|
||||
PeersOut int `json:"peers_out"`
|
||||
|
||||
// Share counters: raw counts (1 submission = 1 share regardless of diff).
|
||||
// Session = since ckpool started; AllTime = persisted across restarts.
|
||||
@@ -151,8 +173,8 @@ const (
|
||||
kvLatencyLastMs = "latency_last_ms"
|
||||
kvStaleWorkHashes = "stale_work_hashes"
|
||||
|
||||
kvAckedBestDiff = "acked_best_diff"
|
||||
kvBestDiff = "best_diff"
|
||||
kvAckedBestDiff = "acked_best_diff"
|
||||
kvBestDiff = "best_diff"
|
||||
kvBestShareHash = "best_share_hash"
|
||||
kvBestShareNetDiff = "best_share_net_diff"
|
||||
|
||||
@@ -186,6 +208,10 @@ type Aggregator struct {
|
||||
// MempoolBaseURL leaves the UI on its mempool.space defaults.
|
||||
MempoolBaseURL string
|
||||
|
||||
// Declared meaning of each ckpool serverurl[] index; see StratumServer.
|
||||
// Nil leaves the UI on its index-1-means-TLS fallback.
|
||||
StratumServers []StratumServer
|
||||
|
||||
// LogFilePath is the ckpool log path, used for one-time backfill
|
||||
// of the best share hash when upgrading from a version that didn't
|
||||
// capture it. Set by main before calling Run.
|
||||
@@ -220,10 +246,10 @@ type Aggregator struct {
|
||||
// post-load observation of pool.Shares only establishes the
|
||||
// baseline — the current ckpool counter was already accounted for
|
||||
// before we crashed.
|
||||
cumulativeShares float64
|
||||
lastPoolShares float64
|
||||
hasPoolSharesBaseline bool
|
||||
lastCumulativeSave time.Time
|
||||
cumulativeShares float64
|
||||
lastPoolShares float64
|
||||
hasPoolSharesBaseline bool
|
||||
lastCumulativeSave time.Time
|
||||
|
||||
// Next-block reward cache; refreshed on its own cadence so we don't
|
||||
// hammer bitcoind with getblocktemplate on every poll tick.
|
||||
@@ -245,8 +271,8 @@ type Aggregator struct {
|
||||
// btcFailStreak counts consecutive refreshes where bitcoind was
|
||||
// unreachable. After a threshold, if no block submission is pending,
|
||||
// we kill the ckpool process so miners can failover to other pools.
|
||||
btcFailStreak int
|
||||
ckpoolKilled bool // true after we sent SIGTERM, reset on bitcoind recovery
|
||||
btcFailStreak int
|
||||
ckpoolKilled bool // true after we sent SIGTERM, reset on bitcoind recovery
|
||||
|
||||
// readyOnce + ready closes the Ready() channel exactly once after
|
||||
// the first refresh completes. main blocks briefly on this so the
|
||||
@@ -401,7 +427,11 @@ func (a *Aggregator) refresh(ctx context.Context) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
next := Snapshot{GeneratedAt: time.Now(), MempoolBaseURL: a.MempoolBaseURL}
|
||||
next := Snapshot{
|
||||
GeneratedAt: time.Now(),
|
||||
MempoolBaseURL: a.MempoolBaseURL,
|
||||
StratumServers: a.StratumServers,
|
||||
}
|
||||
|
||||
// --- ckpool: poolstats, users, workers, clients, uptime ---
|
||||
if ps, err := a.CK.PoolStats(ctx); err == nil {
|
||||
@@ -776,7 +806,6 @@ func diffBucket(d float64) int {
|
||||
// DiffBucketLabels are the human-readable labels for each difficulty bucket.
|
||||
var DiffBucketLabels = [6]string{"< 1M", "1M – 100M", "100M – 1G", "1G – 100G", "100G – 1T", "≥ 1T"}
|
||||
|
||||
|
||||
// IngestShareEvents reads individual share events from the log tailer
|
||||
// and maintains rejection-reason counts and difficulty-distribution
|
||||
// histograms. Session counters reset when ckpool restarts (detected by
|
||||
|
||||
Reference in New Issue
Block a user