Revert block-broadcast fallback path

Removes the entire fallback submitter mechanism: ckpool patch 0004,
the blocksubmit package, the wiring in main.go, the config fields,
the aggregator's fallback counters and snapshot fields, the healthz
fallback fields, and the TS type fields plus the HealthBanners
fallback alert.

Reasoning: ckpool's primary bitcoind submission must remain the
single source of truth, and getting the parallel "race a fallback
during the submit" semantics right is more architectural complexity
than the marginal reliability gain justifies. The original upstream
behavior — submit to bitcoind, retry indefinitely if unavailable —
is what we want.

Kept intact:
  * Submit-attempt vs confirmed counters (block_submit_attempts /
    block_submits_confirmed). Useful on their own as a "did bitcoind
    confirm the submission?" signal.
  * HealthBanners shows submit_gap and zmq_stale only.
  * /healthz exposes submit_gap, zmq_stale, etc.
  * All P0 reliability work (tailer cursor, reconcile loop, reorg
    detection, multi-solve guard) and other P1 (RPC retry, WS
    back-pressure, ZMQ tracking, startup readiness gate).
This commit is contained in:
satoshi
2026-04-28 02:07:17 +03:00
parent 99302cf4af
commit 1dcf087842
9 changed files with 20 additions and 618 deletions
@@ -1,125 +0,0 @@
diff --git a/src/generator.c b/src/generator.c
index 22e2e08..438b199 100644
--- a/src/generator.c
+++ b/src/generator.c
@@ -351,11 +351,25 @@ bool generator_submitblock(ckpool_t *ckp, const char *buf)
server_instance_t *si;
bool warn = false;
connsock_t *cs;
+ /* Bound the wait for current_si so a permanently-down primary
+ * bitcoind does not pin the caller forever. After this many 10ms
+ * sleeps (~3s), return false so the caller (in our fork:
+ * local_block_submit) can hand off to kamado-api's fallback
+ * broadcaster instead of blocking the stratifier on a dead RPC.
+ * Original upstream behavior was to spin indefinitely; we trade
+ * that for predictable latency. */
+ const int max_no_si_iters = 300;
+ int no_si_iters = 0;
while (unlikely(!(si = gdata->current_si))) {
if (!warn)
- LOGWARNING("No live current server in generator_blocksubmit! Resubmitting indefinitely!");
+ LOGWARNING("No live current server in generator_blocksubmit! Waiting up to ~3s before giving up so fallback can take over...");
warn = true;
+ if (++no_si_iters > max_no_si_iters) {
+ LOGWARNING("generator_submitblock: no live primary server after %d ms, returning false",
+ no_si_iters * 10);
+ return false;
+ }
cksleep_ms(10);
}
cs = &si->cs;
diff --git a/src/stratifier.c b/src/stratifier.c
index 52da790..7caf179 100644
--- a/src/stratifier.c
+++ b/src/stratifier.c
@@ -2069,16 +2069,75 @@ process_block(const workbase_t *wb, const char *coinbase, const int cblen,
return gbt_block;
}
-/* Submit block data locally, absorbing and freeing gbt_block */
+/* Write the raw block hex to a sidecar file under <logdir>/pending-blocks/
+ * so the external watcher (kamado-api) can re-broadcast via fallback RPC
+ * nodes. Best-effort: any error here is logged at INFO and never blocks
+ * the caller. Called only after the primary submit fails so the happy
+ * path stays disk-free. */
+static void kamado_dump_pending_block(ckpool_t *ckp, const char *gbt_block,
+ const char *rhash, int height,
+ char *out_path, size_t out_path_len)
+{
+ char dir[512] = {};
+ int fd;
+ size_t blen;
+ ssize_t w;
+
+ out_path[0] = '\0';
+ if (!ckp || !ckp->logdir || !gbt_block)
+ return;
+ snprintf(dir, sizeof(dir), "%spending-blocks", ckp->logdir);
+ if (mkdir(dir, 0750) < 0 && errno != EEXIST) {
+ LOGINFO("kamado: mkdir %s failed: %s", dir, strerror(errno));
+ return;
+ }
+ snprintf(out_path, out_path_len, "%s/%d-%.16s.hex", dir, height, rhash);
+ fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0) {
+ LOGINFO("kamado: open %s failed: %s", out_path, strerror(errno));
+ out_path[0] = '\0';
+ return;
+ }
+ blen = strlen(gbt_block);
+ w = write(fd, gbt_block, blen);
+ close(fd);
+ if (w != (ssize_t)blen) {
+ LOGINFO("kamado: short write to %s (%zd/%zu)", out_path, w, blen);
+ unlink(out_path);
+ out_path[0] = '\0';
+ return;
+ }
+ LOGNOTICE("kamado: dumped pending block height %d to %s (%zu bytes) for fallback broadcast",
+ height, out_path, blen);
+}
+
+/* Submit block data locally, absorbing and freeing gbt_block.
+ *
+ * Order matters: we always try ckp's primary bitcoind FIRST so the
+ * happy path adds zero latency or disk I/O. Only when the primary
+ * rejects (or generator_submitblock returns false for any other
+ * reason) do we dump the raw hex to <logdir>/pending-blocks/ so the
+ * kamado-api watcher can re-broadcast via fallback RPC nodes. */
static bool local_block_submit(ckpool_t *ckp, char *gbt_block, const uchar *flip32, int height)
{
- bool ret = generator_submitblock(ckp, gbt_block);
char heighthash[68] = {}, rhash[68] = {};
+ char pending_path[512] = {};
uchar swap256[32];
+ bool ret;
- free(gbt_block);
swap_256(swap256, flip32);
__bin2hex(rhash, swap256, 32);
+
+ /* Primary submit first — this is the latency-critical path. */
+ ret = generator_submitblock(ckp, gbt_block);
+
+ /* Only touch disk if the primary didn't accept. */
+ if (!ret) {
+ kamado_dump_pending_block(ckp, gbt_block, rhash, height,
+ pending_path, sizeof(pending_path));
+ }
+
+ free(gbt_block);
generator_preciousblock(ckp, rhash);
/* Check failures that may be inconclusive but were submitted via other
@@ -2099,6 +2158,10 @@ static bool local_block_submit(ckpool_t *ckp, char *gbt_block, const uchar *flip
height, ret ? "ACCEPTED" : "REJECTED");
}
}
+ /* Block ended up on chain (either initial submit or precious-block
+ * recovery): the dump file is no longer needed. */
+ if (ret && pending_path[0])
+ unlink(pending_path);
return ret;
}
+1 -22
View File
@@ -9,7 +9,7 @@ Patches are applied in alphabetical order by filename. Use a numeric prefix to e
## Current state
Four Kamado patches are applied on top of the pinned upstream commit, in
Three Kamado patches are applied on top of the pinned upstream commit, in
alphabetical order:
| Patch | What it does |
@@ -17,7 +17,6 @@ alphabetical order:
| `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
@@ -34,26 +33,6 @@ 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: