ix ckpool.conf render dropping the TLS bind port; Let deployments declare what each stratum bind is
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Formatters for hashrate, share difficulty, time, and miner hardware
|
||||
// detection from stratum user-agent strings. Pure functions, no DOM.
|
||||
|
||||
import type { StratumServer } from "./types";
|
||||
|
||||
const HASHRATE_UNITS = ["H/s", "kH/s", "MH/s", "GH/s", "TH/s", "PH/s", "EH/s"];
|
||||
|
||||
export function formatHashrate(hs: number): string {
|
||||
@@ -100,6 +102,26 @@ export function explorerBaseFor(
|
||||
return "https://mempool.space";
|
||||
}
|
||||
|
||||
// Resolve how a miner is connected from its ckpool serverurl[] index.
|
||||
//
|
||||
// The deployment declares the bind array via STRATUM_SERVERS (the StartOS
|
||||
// wrapper renders one entry per bind it configures). When it hasn't — the
|
||||
// dev compose stack, or an older wrapper — fall back to the historical
|
||||
// layout, where index 0 is the plaintext bind and anything else is the
|
||||
// loopback bind that stunnel forwards TLS traffic to.
|
||||
export function connectionOf(
|
||||
serverIndex: number | undefined,
|
||||
servers: StratumServer[] | undefined,
|
||||
): { tls: boolean; label: string } {
|
||||
const declared = servers?.[serverIndex ?? 0];
|
||||
if (declared) {
|
||||
return { tls: declared.kind !== "plain", label: declared.label };
|
||||
}
|
||||
return serverIndex
|
||||
? { tls: true, label: "Encrypted connection via TLS" }
|
||||
: { tls: false, label: "Unencrypted connection" };
|
||||
}
|
||||
|
||||
// Detect common Bitcoin mining hardware from the stratum useragent
|
||||
// string. This is a best-effort heuristic; unknown agents fall back
|
||||
// to a stripped version of the raw string. Order matters — check
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
detectHardware,
|
||||
isOpenSource,
|
||||
btcAddressOf,
|
||||
connectionOf,
|
||||
} from "../format";
|
||||
import type { StratumClient, Worker } from "../types";
|
||||
|
||||
@@ -18,6 +19,7 @@
|
||||
hardware: string;
|
||||
openSource: boolean;
|
||||
tls: boolean;
|
||||
tlsLabel: string;
|
||||
hashrate1m: number;
|
||||
hashrate1h: number;
|
||||
bestSession: number;
|
||||
@@ -35,9 +37,10 @@
|
||||
// from the joined worker_instance, which ckpool keys by full
|
||||
// workername with user = the BTC.
|
||||
//
|
||||
// ckpool's serverurl array gives us a second piece of info: TLS
|
||||
// traffic comes in via stunnel on the loopback-only bind (server
|
||||
// index 1), so client.server === 1 means this miner is using TLS.
|
||||
// ckpool's serverurl array gives us a second piece of info: which
|
||||
// bind the connection arrived on. connectionOf() maps that index to
|
||||
// the transport the deployment declared, so the lock badge can name
|
||||
// the certificate in play rather than just "encrypted".
|
||||
const rows = $derived.by<Row[]>(() => {
|
||||
const clients = snap.data?.clients ?? [];
|
||||
const workers = snap.data?.workers ?? [];
|
||||
@@ -52,13 +55,15 @@
|
||||
seen.add(wname);
|
||||
const w = byWorker.get(wname);
|
||||
const btcAddress = w?.user ?? btcAddressOf(wname);
|
||||
const conn = connectionOf(c.server, snap.data?.stratum_servers);
|
||||
out.push({
|
||||
workerName: wname,
|
||||
btcAddress,
|
||||
sourceIp: c.address,
|
||||
hardware: detectHardware(c.useragent),
|
||||
openSource: isOpenSource(c.useragent),
|
||||
tls: c.server === 1,
|
||||
tls: conn.tls,
|
||||
tlsLabel: conn.label,
|
||||
hashrate1m: c.dsps1 * 2 ** 32,
|
||||
hashrate1h: c.dsps60 * 2 ** 32,
|
||||
bestSession: c.bestdiff,
|
||||
@@ -79,6 +84,7 @@
|
||||
hardware: "offline",
|
||||
openSource: false,
|
||||
tls: false,
|
||||
tlsLabel: "",
|
||||
hashrate1m: 0,
|
||||
hashrate1h: 0,
|
||||
bestSession: 0,
|
||||
@@ -133,7 +139,7 @@
|
||||
title="View per-worker stats"
|
||||
>{r.workerName}</button>
|
||||
{#if r.tls}
|
||||
<span class="badge tls" title="Encrypted connection via TLS">
|
||||
<span class="badge tls" title={r.tlsLabel}>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7H4a1.5 1.5 0 0 0-1.5 1.5v5A1.5 1.5 0 0 0 4 15h8a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 12 7h-.5V4.5A3.5 3.5 0 0 0 8 1Zm2 6H6V4.5a2 2 0 1 1 4 0V7Z"/>
|
||||
</svg>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
detectHardware,
|
||||
isOpenSource,
|
||||
explorerBaseFor,
|
||||
connectionOf,
|
||||
} from "../format";
|
||||
import type { Worker, StratumClient } from "../types";
|
||||
|
||||
@@ -46,6 +47,7 @@
|
||||
hardware: string;
|
||||
openSource: boolean;
|
||||
tls: boolean;
|
||||
tlsLabel: string;
|
||||
sourceIp: string;
|
||||
hashrate1m: number;
|
||||
hashrate1h: number;
|
||||
@@ -64,7 +66,10 @@
|
||||
worker: w.worker,
|
||||
hardware: c ? detectHardware(c.useragent) : "offline",
|
||||
openSource: c ? isOpenSource(c.useragent) : false,
|
||||
tls: c?.server === 1,
|
||||
tls: !!c && connectionOf(c.server, snap.data?.stratum_servers).tls,
|
||||
tlsLabel: c
|
||||
? connectionOf(c.server, snap.data?.stratum_servers).label
|
||||
: "",
|
||||
sourceIp: c?.address ?? "",
|
||||
hashrate1m: c ? c.dsps1 * 2 ** 32 : 0,
|
||||
hashrate1h: c ? c.dsps60 * 2 ** 32 : 0,
|
||||
@@ -85,7 +90,8 @@
|
||||
worker: wname,
|
||||
hardware: detectHardware(c.useragent),
|
||||
openSource: isOpenSource(c.useragent),
|
||||
tls: c.server === 1,
|
||||
tls: connectionOf(c.server, snap.data?.stratum_servers).tls,
|
||||
tlsLabel: connectionOf(c.server, snap.data?.stratum_servers).label,
|
||||
sourceIp: c.address,
|
||||
hashrate1m: c.dsps1 * 2 ** 32,
|
||||
hashrate1h: c.dsps60 * 2 ** 32,
|
||||
@@ -232,7 +238,7 @@
|
||||
title="View per-worker stats"
|
||||
>{r.worker}</button>
|
||||
{#if r.tls}
|
||||
<span class="badge tls" title="Encrypted connection via TLS">
|
||||
<span class="badge tls" title={r.tlsLabel}>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7H4a1.5 1.5 0 0 0-1.5 1.5v5A1.5 1.5 0 0 0 4 15h8a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 12 7h-.5V4.5A3.5 3.5 0 0 0 8 1Zm2 6H6V4.5a2 2 0 1 1 4 0V7Z"/>
|
||||
</svg>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
detectHardware,
|
||||
isOpenSource,
|
||||
btcAddressOf,
|
||||
connectionOf,
|
||||
} from "../format";
|
||||
import type { Worker, StratumClient } from "../types";
|
||||
|
||||
@@ -29,7 +30,12 @@
|
||||
const idle = $derived(client?.idle ?? worker?.idle ?? false);
|
||||
const hardware = $derived(client ? detectHardware(client.useragent) : "offline");
|
||||
const openSource = $derived(client ? isOpenSource(client.useragent) : false);
|
||||
const tls = $derived(client?.server === 1);
|
||||
const conn = $derived(
|
||||
client
|
||||
? connectionOf(client.server, snap.data?.stratum_servers)
|
||||
: { tls: false, label: "" },
|
||||
);
|
||||
const tls = $derived(conn.tls);
|
||||
|
||||
const hs1m = $derived(client ? client.dsps1 * 2 ** 32 : 0);
|
||||
const hs5m = $derived(client ? client.dsps5 * 2 ** 32 : 0);
|
||||
@@ -88,7 +94,7 @@
|
||||
title="View per-user stats"
|
||||
>{btcAddress}</button>
|
||||
{#if tls}
|
||||
<span class="badge tls" title="Encrypted connection via TLS">
|
||||
<span class="badge tls" title={conn.label}>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7H4a1.5 1.5 0 0 0-1.5 1.5v5A1.5 1.5 0 0 0 4 15h8a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 12 7h-.5V4.5A3.5 3.5 0 0 0 8 1Zm2 6H6V4.5a2 2 0 1 1 4 0V7Z"/>
|
||||
</svg>
|
||||
|
||||
+18
-3
@@ -71,9 +71,10 @@ export type StratumClient = {
|
||||
workername: string;
|
||||
userid: number;
|
||||
// Index into ckpool's serverurl[] array; identifies which stratum
|
||||
// bind the client connected on. We use this to tag TLS clients:
|
||||
// index 0 is the public plaintext bind, index 1 is the loopback-only
|
||||
// bind that stunnel forwards TLS traffic to.
|
||||
// bind the client connected on. What each index *means* depends on
|
||||
// how the deployment rendered ckpool.conf, so resolve it through
|
||||
// Snapshot.stratum_servers rather than hardcoding — see
|
||||
// connectionOf() in format.ts.
|
||||
server: number;
|
||||
bestdiff: number;
|
||||
};
|
||||
@@ -112,6 +113,16 @@ export type HashratePoint = {
|
||||
v: number; // H/s
|
||||
};
|
||||
|
||||
// One entry per ckpool serverurl[] bind, declared by the deployment via
|
||||
// the STRATUM_SERVERS env. `kind` is the stable tag the UI switches on;
|
||||
// `label` is free text shown on hover.
|
||||
export type StratumServerKind = "plain" | "tls-local" | "tls-public";
|
||||
|
||||
export type StratumServer = {
|
||||
kind: StratumServerKind;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type Snapshot = {
|
||||
generated_at: string;
|
||||
pool: PoolStats | null;
|
||||
@@ -137,6 +148,10 @@ export type Snapshot = {
|
||||
// Optional override for explorer links. Empty/undefined means the
|
||||
// UI falls back to its mempool.space defaults.
|
||||
mempool_base_url?: string;
|
||||
// Describes ckpool's serverurl[] binds: entry N says what a client
|
||||
// with `server === N` is connected over. Undefined when the
|
||||
// deployment didn't declare them — see connectionOf() in format.ts.
|
||||
stratum_servers?: StratumServer[];
|
||||
// Submission attempt tracking — count of "Possible block solve"
|
||||
// log lines vs "Solved and confirmed" lines. A growing gap means
|
||||
// submissions are being rejected by bitcoind.
|
||||
|
||||
Reference in New Issue
Block a user