Add share cards

This commit is contained in:
satoshi committed 2026-09-04 00:54:26 +03:00
1 parent ec42ce9b54
commit 4669bf10c0
51 files changed
+1193 -239

No files matched your search

+10 -10
View File
@@ -1,4 +1,4 @@
// Package logmon tails the ckpool log file looking for notable events —
// Package logmon tails the ckpool log file looking for notable events,
// primarily block-solve lines, which are the most reliable signal we have
// that a block was found, short of a ZMQ hashblock subscription.
//
@@ -39,7 +39,7 @@ type BlockEvent struct {
}
// AttemptEvent is emitted when ckpool logs a "Possible block solve"
// or "Submitting possible block solve" line — i.e. ckpool decided a
// or "Submitting possible block solve" line, i.e. ckpool decided a
// share met network difficulty and is attempting to submit it to
// bitcoind. This precedes the "Solved and confirmed" line that drives
// BlockEvent. Counting attempts vs confirmations surfaces submission
@@ -52,7 +52,7 @@ type AttemptEvent struct {
}
// LatencyEvent is emitted when ckpool logs the "Block update latency"
// line from our patch 0005 — the milliseconds between ZMQ trigger and
// line from our patch 0005, the milliseconds between ZMQ trigger and
// mining.notify broadcast completion.
type LatencyEvent struct {
SeenAt time.Time
@@ -156,8 +156,8 @@ var (
// The first number after "diff " is sdiff (share difficulty solved).
acceptedShareRE = regexp.MustCompile(`Accepted client \S+ share diff ([0-9.]+)/[^:]+:\s*([0-9a-fA-F]+)`)
// Rejected shares come in two forms:
// 1. "dupe diff" / "high diff" — share was valid but duplicate or below target
// 2. "invalid share <reason>" — share was structurally invalid (Stale, etc.)
// 1. "dupe diff" / "high diff", share was valid but duplicate or below target
// 2. "invalid share <reason>", share was structurally invalid (Stale, etc.)
rejectedDiffRE = regexp.MustCompile(`Rejected client \S+ (\w+) diff [0-9.]+/`)
rejectedInvalRE = regexp.MustCompile(`Rejected client \S+ invalid share (.+)`)
)
@@ -171,7 +171,7 @@ const powDataMarker = "Best share PoW data "
// parsePowLine extracts the PoW record from a log line carrying
// powDataMarker. It decodes exactly one JSON value starting at the first
// '{' after the marker and deliberately ignores everything after it —
// '{' after the marker and deliberately ignores everything after it,
// production logs have carried trailing bytes after the closing brace,
// which silently defeated an end-of-line-anchored regex. Returns
// (nil, nil) when the marker is absent, and a non-nil error for a
@@ -368,7 +368,7 @@ func (t *Tailer) handleLine(line string) {
default:
}
}
// Don't return — an accepted share may also be a block solve,
// Don't return, an accepted share may also be a block solve,
// so let the solve-diff regex below get a chance to match too.
}
// Rejected share with diff info: "Rejected client <id> dupe|high diff ..."
@@ -407,7 +407,7 @@ func (t *Tailer) handleLine(line string) {
select {
case t.Attempts <- AttemptEvent{SeenAt: time.Now(), ShareDiff: d, RawLine: line}:
default:
// Best-effort metric — dropping is fine.
// Best-effort metric, dropping is fine.
}
return
}
@@ -484,7 +484,7 @@ func FindBestShareHash(path string) (float64, string) {
// of any length can never abort the scan: an oversized line is passed to
// fn truncated to the buffer size and its remainder is discarded. A
// months-old production log with one pathological line must not silently
// stop a backfill scan partway through — that failure mode is invisible
// stop a backfill scan partway through, that failure mode is invisible
// and permanent.
func scanLines(f *os.File, fn func(line string)) error {
r := bufio.NewReaderSize(f, 256*1024)
@@ -513,7 +513,7 @@ var logStampRE = regexp.MustCompile(`^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})`)
// by an "Accepted client" line with the same hash. Used as a one-time
// backfill when kamado-api holds no persisted record but the log
// already contains some (fresh database, or PoW events dropped under
// load) — without it the PoW inspector would sit empty until a share
// load), without it the PoW inspector would sit empty until a share
// beat ckpool's in-process best, which after a long uptime can take
// arbitrarily long. The returned time is parsed from the log line's
// timestamp; zero when unparseable. Returns a nil record if no confirmed
+3 -3
View File
@@ -77,7 +77,7 @@ func TestHandleLine_DiffResetAfterBlock(t *testing.T) {
tl := New("/unused", discardLog())
tl.handleLine("Possible block solve diff 16384 !\n")
tl.handleLine("Solved and confirmed block 100\n")
// Second block — no preceding attempt line; diff must NOT carry over.
// Second block, no preceding attempt line; diff must NOT carry over.
tl.handleLine("Solved and confirmed block 101\n")
drain := func() BlockEvent {
@@ -217,7 +217,7 @@ func TestFindBestPowData(t *testing.T) {
// Production ckpool logs have carried bytes after the JSON's closing
// brace (observed in the field: an end-of-line-anchored regex matched 0
// of 54 real PoW lines while synthetic lines passed). The parser must
// decode the JSON value and ignore ANY trailing bytes — NULs, \r,
// decode the JSON value and ignore ANY trailing bytes, NULs, \r,
// or an interleaved second message on the same line.
func TestHandleLine_PowDataTrailingGarbage(t *testing.T) {
suffixes := []string{
@@ -240,7 +240,7 @@ func TestHandleLine_PowDataTrailingGarbage(t *testing.T) {
}
// A pathologically long line earlier in the log must not abort the scan
// before it reaches the PoW records — bufio.Scanner did exactly that,
// before it reaches the PoW records, bufio.Scanner did exactly that,
// silently, which left the backfill permanently empty on real logs.
func TestFindBestPowData_SurvivesOversizedLine(t *testing.T) {
path := tempLog(t)