Record and display winning share difficulty on found blocks

Logmon now captures the share diff from ckpool's "Possible block
solve" line preceding the confirmation and attaches it to the
BlockEvent. Persisted as share_diff alongside height/hash/reward
and rendered as a new column in the dashboard block history.
This commit is contained in:
satoshi
2026-04-14 18:15:44 +03:00
parent 89904d5e08
commit 64d8af407d
5 changed files with 112 additions and 44 deletions
+36 -7
View File
@@ -26,11 +26,14 @@ import (
// BlockEvent is emitted when the tailer sees a "Solved and confirmed block"
// line in the ckpool log. Hash and worker are populated later by the
// aggregator once it cross-references bitcoind.
// aggregator once it cross-references bitcoind. ShareDiff is the
// difficulty of the winning share, captured from the "Possible block
// solve" line that precedes the confirmation line.
type BlockEvent struct {
Height int64 `json:"height"`
SeenAt time.Time `json:"seen_at"`
RawLine string `json:"raw_line"`
Height int64 `json:"height"`
SeenAt time.Time `json:"seen_at"`
RawLine string `json:"raw_line"`
ShareDiff float64 `json:"share_diff,omitempty"`
}
// Tailer follows a log file, surviving rotation/truncation, and emits
@@ -40,6 +43,11 @@ type Tailer struct {
Events chan BlockEvent
Log *slog.Logger
PollWait time.Duration // how long to sleep between EOF polls
// lastSolveDiff remembers the share diff from the most recent
// "Possible block solve" line so handleLine can attach it to the
// subsequent "Solved and confirmed block" event. Reset after use.
lastSolveDiff float64
}
func New(path string, log *slog.Logger) *Tailer {
@@ -51,7 +59,16 @@ func New(path string, log *slog.Logger) *Tailer {
}
}
var solvedRE = regexp.MustCompile(`Solved and confirmed block\s+(\d+)`)
var (
solvedRE = regexp.MustCompile(`Solved and confirmed block\s+(\d+)`)
// Matches the three "Possible ... block solve ... diff <float>" lines
// ckpool emits from stratifier.c right before a block is submitted:
// "Possible block solve diff N !"
// "Possible stale share block solve diff N !"
// "Submitting possible block solve share diff N !"
// "Possible remote block solve diff N !"
solveDiffRE = regexp.MustCompile(`(?:Possible|Submitting[^"]*possible).*block solve.*diff\s+([0-9eE.+-]+)`)
)
// Run blocks until ctx is cancelled. It opens the file, seeks to the end,
// and reads new lines as they are appended. If the file is rotated
@@ -146,6 +163,12 @@ func (t *Tailer) Run(ctx context.Context) {
}
func (t *Tailer) handleLine(line string) {
if m := solveDiffRE.FindStringSubmatch(line); m != nil {
if d, err := strconv.ParseFloat(m[1], 64); err == nil {
t.lastSolveDiff = d
}
return
}
m := solvedRE.FindStringSubmatch(line)
if m == nil {
return
@@ -154,10 +177,16 @@ func (t *Tailer) handleLine(line string) {
if err != nil {
return
}
ev := BlockEvent{Height: height, SeenAt: time.Now(), RawLine: line}
ev := BlockEvent{
Height: height,
SeenAt: time.Now(),
RawLine: line,
ShareDiff: t.lastSolveDiff,
}
t.lastSolveDiff = 0
select {
case t.Events <- ev:
t.Log.Info("logmon: block solved", "height", height)
t.Log.Info("logmon: block solved", "height", height, "share_diff", ev.ShareDiff)
default:
t.Log.Warn("logmon: events channel full, dropping", "height", height)
}