Files
KamadoPool/ckpool/patches
satoshi a4a894e196 P1 reliability + block-broadcast fallback path
P1 audits / fixes:

* Bitcoin Core RPC now retries up to 3 times with linear backoff on
  transport errors, 5xx responses, and warm-up/loading RPC errors
  (code -28). Hard "no" answers (block-not-found etc.) bubble up
  immediately so we don't mask real errors.

* WebSocket hub disconnects clients that miss 6 consecutive broadcasts
  (~30s with the default poll cadence). Stuck readers no longer hold
  stale snapshots indefinitely or freeze hub state.

* ZMQ subscriber freshness: aggregator records the last-event
  timestamp, surfaces zmq_enabled / has_last_zmq_event /
  last_zmq_event_age in the snapshot. /healthz flags zmq_stale when
  the gap exceeds 30 minutes.

* /healthz expanded with submit_attempts / submits_confirmed /
  submit_gap, fallback_submits_total + last_fallback_*, and the zmq
  staleness check. Now usable as a real-world ops dashboard signal.

Block-broadcast fallback (new feature):

  * ckpool patch 0004: hooks local_block_submit to write the raw block
    hex to <logdir>/pending-blocks/<height>-<hash16>.hex right before
    invoking generator_submitblock. Unlinks on success. ckpool's normal
    flow is otherwise untouched.

  * api/internal/blocksubmit: watcher polls the dir every 5s. Files
    sitting longer than the grace window (default 30s, configurable)
    are re-broadcast through operator-supplied backup RPC URLs in
    sequence. Treats both null and any "duplicate*" reject reason as
    success (the block landed). Pre-checks the primary chain first so
    a stale file from a successful-but-unlinked submit gets cleaned
    up without bothering fallbacks.

  * Aggregator records each successful fallback submission as a
    persistent counter and surfaces it in the snapshot so the UI can
    show a "primary bitcoind isn't accepting submits" alert.

  * Config: BACKUP_RPC_URLS (comma- or newline-separated, with
    optional inline credentials) plus PENDING_BLOCKS_DIR and
    PENDING_BLOCKS_GRACE. URLs are parsed via net/url so
    https://user:pass@host:port/ works cleanly.

The fallback is opt-in and disabled by default. Once enabled with at
least one URL, a primary bitcoind outage at the moment of solving no
longer means a lost block — kamado-api re-broadcasts via whichever
backup the operator trusts (a second self-hosted node, an
authenticated public RPC service, etc.).
2026-04-27 21:25:56 +03:00
..

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.patch
  • 0002-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:

  1. Fixes needed before they land upstream (and only after attempting to submit upstream first).
  2. Kamado-specific behavior changes that would not be accepted upstream — e.g. tighter integration hooks with kamado-api.
  3. 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.