Track cumulative work via accounted_diff_shares, surface round effort
Total work was summing pool.Shares (ckpool's accounted_shares — raw per-share count) and multiplying by 2^32, which is nonsense: each share's actual difficulty was ignored, so a pool running at any real hashrate would show a tiny number. Switch to pool.Accepted, which ckpool exposes as accounted_diff_shares (sum of each accepted share's difficulty in diff-1-normalized units). cumulative_shares * 2^32 is now actually total hashes. Bump the kv key from "cumulative_shares" to "cumulative_work" so any value saved under the old name is orphaned rather than mixed into the new (correctly-unit'd) counter on upgrade. Total work tile's sub-info now shows the round effort: cumulative_shares / network_difficulty * 100. Matches ckpool's own formula at stratifier.c:8201 for the percent-of-block display. Drop the duplicate effort text from the Expected block tile, which was an uptime-based approximation of the same thing.
This commit is contained in:
@@ -76,7 +76,13 @@ type Snapshot struct {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
maxHistoryPoints = 1440 // 24h at 1 sample/min
|
maxHistoryPoints = 1440 // 24h at 1 sample/min
|
||||||
kvCumulativeWork = "cumulative_shares"
|
|
||||||
|
// kvCumulativeWork is versioned: "cumulative_shares" (v1) accidentally
|
||||||
|
// summed pool.shares (raw per-share count), which is meaningless for
|
||||||
|
// hashrate math. "cumulative_work" (v2) sums pool.accepted — the
|
||||||
|
// diff-1-normalized work, so cumulative_work * 2^32 is real hashes.
|
||||||
|
// The old key is orphaned in the kv table on upgrade; harmless.
|
||||||
|
kvCumulativeWork = "cumulative_work"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Aggregator refreshes a Snapshot on a ticker.
|
// Aggregator refreshes a Snapshot on a ticker.
|
||||||
@@ -285,13 +291,16 @@ func (a *Aggregator) refresh(ctx context.Context) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// --- cumulative work tracking ---
|
// --- cumulative work tracking ---
|
||||||
// Integrate only positive deltas on pool.Shares. A decrease means
|
// Integrate only positive deltas on pool.Accepted, which is ckpool's
|
||||||
// ckpool restarted (or its state reset); reset the baseline without
|
// accounted_diff_shares — the sum of each accepted share's
|
||||||
// touching the accumulated total. The first observation after load
|
// difficulty, in diff-1-normalized units. pool.Shares is the raw
|
||||||
// only establishes the baseline — those shares were already
|
// per-share count and is useless for hashrate math. A decrease
|
||||||
// accumulated before the previous shutdown.
|
// means ckpool restarted (or its state reset); we reset the
|
||||||
|
// baseline without touching the accumulated total. The first
|
||||||
|
// observation after load only establishes the baseline — the
|
||||||
|
// current counter was already integrated before we shut down.
|
||||||
if next.Pool != nil {
|
if next.Pool != nil {
|
||||||
cur := float64(next.Pool.Shares)
|
cur := float64(next.Pool.Accepted)
|
||||||
if a.hasPoolSharesBaseline && cur >= a.lastPoolShares {
|
if a.hasPoolSharesBaseline && cur >= a.lastPoolShares {
|
||||||
a.cumulativeShares += cur - a.lastPoolShares
|
a.cumulativeShares += cur - a.lastPoolShares
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,14 @@
|
|||||||
return expectedBlockSeconds(data.hashrate_hs_1m, data.network_hashrate_hs, diff);
|
return expectedBlockSeconds(data.hashrate_hs_1m, data.network_hashrate_hs, diff);
|
||||||
});
|
});
|
||||||
|
|
||||||
const effort = $derived.by(() => {
|
// Round effort: share of the expected work-to-find-a-block that the
|
||||||
if (!isFinite(expected) || expected <= 0) return 0;
|
// pool has already submitted, at the current network difficulty.
|
||||||
const uptime = data.uptime_seconds;
|
// Matches ckpool's own accounted_diff_shares / network_diff * 100
|
||||||
if (!uptime || uptime <= 0) return 0;
|
// (stratifier.c:8201).
|
||||||
return (uptime / expected) * 100;
|
const roundEffort = $derived.by(() => {
|
||||||
|
const d = data.chain?.difficulty ?? 0;
|
||||||
|
if (!d || d <= 0) return 0;
|
||||||
|
return (data.cumulative_shares / d) * 100;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Blocks remaining + ETA until the retarget. The predicted adjustment
|
// Blocks remaining + ETA until the retarget. The predicted adjustment
|
||||||
@@ -121,15 +124,15 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="stat-label">Total work</div>
|
<div class="stat-label">Total work</div>
|
||||||
<div class="stat-value">{formatWork(totalHashes)}</div>
|
<div class="stat-value">{formatWork(totalHashes)}</div>
|
||||||
<div class="stat-sub">hashes submitted by pool</div>
|
<div class="stat-sub">
|
||||||
|
round effort {roundEffort.toFixed(1)}% of current difficulty
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="stat-label">Expected block</div>
|
<div class="stat-label">Expected block</div>
|
||||||
<div class="stat-value">{formatDuration(expected)}</div>
|
<div class="stat-value">{formatDuration(expected)}</div>
|
||||||
<div class="stat-sub">
|
<div class="stat-sub">uptime {formatUptime(data.uptime_seconds)}</div>
|
||||||
effort {effort.toFixed(1)}% · uptime {formatUptime(data.uptime_seconds)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|||||||
Reference in New Issue
Block a user