Submit-first ordering. Patch 0004 now calls generator_submitblock
BEFORE writing the pending-block hex to disk. The happy path adds zero
disk I/O — we only dump when the primary returned false. The same
patch bounds generator_submitblock's "no live current_si" spin to
~3s instead of the original infinite loop, so a permanently-down
primary doesn't pin the stratifier; the bounded spin lets the caller
return false and lets local_block_submit dump for kamado-api to take
over.
Default grace lowered from 30s to 3s. With ckpool's bounded spin and
sub-second sweep cadence, the fallback now reacts within ~4s of a
failed primary submit — fast enough that the work is still relevant
for the current chain tip. The submitter's sweep poll dropped to 1s
to match.
UI HealthBanners. New top-of-page strip surfaces:
* Fallback used (red banner, 24h after most recent event):
"primary bitcoind didn't accept; backup X took over Y ago"
* Submit gap (orange banner, only when no recent fallback):
"N blocks attempted but unconfirmed — configure backups"
* ZMQ stale (orange banner): no hashblock frame in 30+ minutes
Operators see degraded-but-not-fatal states without checking logs.
Startup readiness gate. main now waits up to 8s on agg.Ready() before
starting the HTTP server so the very first /api/snapshot doesn't show
all-zero state during the aggregator's first refresh. Capped so a
permanently-down bitcoind can't block startup; /healthz is honest
about the degraded state once we do start serving.
CKPool Patches
Patches applied on top of the upstream CKPool commit pinned in ../CKPOOL_COMMIT.
Patches are applied in alphabetical order by filename. Use a numeric prefix to enforce ordering:
0001-short-description.patch0002-another-fix.patch
Current state
Four Kamado patches are applied on top of the pinned upstream commit, in alphabetical order:
| Patch | What it does |
|---|---|
0001-expose-bestever-in-runtime-json.patch |
Adds bestever field to the users / workers runtime socket JSON |
0002-enable-socket-api-responses.patch |
Always reply on the listener socket so kamado-api gets responses even with btcsolo: true |
0003-share-error-as-stratum-array.patch |
Maps share_err to Stratum spec error codes; emits [code, msg, null] per Slush |
0004-dump-pending-block-for-fallback.patch |
Writes the raw block hex to <logdir>/pending-blocks/ before submit; unlinks on success |
Why 0001 matters
Upstream tracks best_ever internally in user_instance_t / worker_instance_t
and zeroes best_diff on every block solve via reset_bestshares(). That
is correct: bestdiff is "best share in the current round". But the runtime
socket API (userinfo() / workerinfo() in stratifier.c) only emits
bestdiff, so any consumer that talks to the socket — like kamado-api —
sees the best share reset to 0 after every block and has no all-time field
to fall back on. The on-disk users.json / workers.json persistence files
do include bestever, but polling those is racy and lags the socket.
This patch adds bestever to the runtime JSON so the UI can show both
"this round" and "all-time" best share side by side. No behavioral change
to share validation or block handling. Candidate for upstreaming.
Why 0004 matters
Block submission to bitcoind is the most revenue-critical RPC call ckpool
makes. If bitcoind is unreachable when a share meets network difficulty,
ckpool's generator thread retries indefinitely against the same single
endpoint — and the raw block data lives only in stratifier memory, so a
ckpool crash before bitcoind comes back permanently loses the block.
This patch hooks local_block_submit to write the raw block hex to
<logdir>/pending-blocks/<height>-<rhash>.hex before invoking
generator_submitblock, and unlinks the file on success. kamado-api
runs a watcher over that directory: if a file persists past a grace
period (default 30 s), it submits the block via fallback RPC URLs the
operator has configured. Multiple fallbacks are tried in sequence; the
file is unlinked when any fallback returns success or "duplicate"
(meaning the block already landed).
This is a Kamado-specific integration hook — almost certainly not upstreamable, but minimal-impact on existing ckpool behavior.
Beyond this patch, the pinned upstream commit (cfb0f83b, tagged as
version 1.0) already includes every fix that Bassin issue #29 asked to
backport, plus several improvements:
| Upstream commit | What it fixes |
|---|---|
a439cf96 |
workbase_id double increment bug |
590fb2a2 |
Extended timeouts for low-powered miners (NerdMiner, ESP32) |
b13f3eee |
Configurable dropidle timeout (exposed via our env var) |
66db3aa3 |
Better vardiff for bursty hashers |
130c755d |
Fix unlikely fopen segfault |
0bd3d751 |
Consistent error field in mining.submit rejections |
988b2687 |
Version 1.0 — longstanding stability bump |
Kamado therefore starts from a CKPool that is strictly ahead of what Bassin ships today.
When to add a patch
Use this directory for:
- Fixes needed before they land upstream (and only after attempting to submit upstream first).
- Kamado-specific behavior changes that would not be accepted upstream —
e.g. tighter integration hooks with
kamado-api. - Temporary workarounds with a clear removal plan, documented in the patch commit message.
Do NOT use this directory for:
- Pure configuration changes (expose via the entrypoint env vars instead).
- Build system tweaks (put those in the Dockerfile).
Creating a patch
From a clean clone of upstream at the pinned commit:
git clone https://bitbucket.org/ckolivas/ckpool.git
cd ckpool
git checkout $(cat /path/to/KamadoPool/ckpool/CKPOOL_COMMIT)
# ... make your changes ...
git diff > /path/to/KamadoPool/ckpool/patches/0001-my-fix.patch
The Dockerfile applies each *.patch file in this directory with
git apply --verbose during the build.