Closes the silent-failure modes between "ckpool logs a solve" and
"block correctly displayed":
* Difficulty estimate matched mempool.space — the projection now uses
(inEpoch + 1) intervals so it converges on Bitcoin Core's eventual
retarget formula at end-of-epoch instead of undershooting by ~0.05–
0.10 % throughout.
* Tailer resumes mid-log on restart — persists (inode, offset) to kv
every EOF + on shutdown, and replays the unread tail next time. Any
solve line written while kamado-api was down would previously be
invisible forever.
* Background reconcile loop (60 s) retries hash/reward enrichment for
blocks the original RPC missed, so a transient bitcoind-index race no
longer permanently leaves a block hashless.
* Reorg detection: same loop compares each recent stored hash against
getblockhash(height); a mismatch stamps orphaned_at. UI renders these
strikethrough with a red "orphaned" tag instead of showing illusory
rewards forever.
* InsertBlock now reports whether a row was actually inserted; the
caller WARN-logs duplicate-height ignores so a re-mined orphaned
height can't disappear silently.
* Submit-attempt vs confirmed counters surface failed submissions:
every "Possible/Submitting block solve" log line increments
block_submit_attempts; "Solved and confirmed" increments
block_submits_confirmed. A growing gap means bitcoind is rejecting
our submissions — previously invisible.
* share_err patch refreshed against pinned ckpool source: added
SE_NO_JOBID -> 21 and SE_WORKER_MISMATCH -> 24 mappings, kept
SE_INVALID_NONCE2 in 20 (it's a malformed-input error, not low-diff).
AxeOS users now see actionable Stratum codes instead of
"unknown error".
UI gets new orphaned_at + block_submit_attempts/confirmed fields on
the snapshot type and a strikethrough-with-tag rendering for orphaned
blocks in BlocksTable.
ckpool's share-rejection response sets the "error" field to a bare
JSON string ("Stale", "Above target", "Duplicate", ...). The
Stratum mining v1 spec, and every miner firmware that follows it,
expects that field to be a [code, message, traceback] array. AxeOS
parses with cJSON_GetArrayItem after a cJSON_IsArray check, sees a
string, falls back to "unknown error", and the rejection reason
disappears from the dashboard. Bassin / public-pool sends the
array form, which is why those rejections render as "stale" there.
Patch the JSON_ERR macro in stratifier.c to wrap the existing
share_errs[] string in json_pack("[isn]", ...) and add a small
share_err_code() helper mapping ckpool's enum to the standard
Slush stratum codes:
21 stale (SE_STALE, SE_INVALID_JOBID, SE_NTIME_INVALID)
22 duplicate (SE_DUPE)
23 low diff (SE_HIGH_DIFF)
24 unauthorized (SE_NO_USERNAME)
20 other (everything else)
All 14 JSON_ERR call sites in parse_submit pick this up
automatically — no other call site changes needed. Generated with
git diff against the pinned upstream commit so format is correct;
round-trip-tested with git apply --check.