Patch 9: fix shares being rejected after a pool restart bug
This commit is contained in:
2 files changed
+120
-1
No files matched your search
@@ -32,7 +32,7 @@ CKPool is a high-performance stratum server, but it has no web interface and lim
|
||||
|
||||
### Patches Applied to CKPool
|
||||
|
||||
Eight patches are applied to upstream commit `cfb0f83` (which itself includes the workbase_id fix, extended ESP32/NerdMiner timeouts, configurable `dropidle`, and vardiff improvements):
|
||||
Nine patches are applied to upstream commit `cfb0f83` (which itself includes the workbase_id fix, extended ESP32/NerdMiner timeouts, configurable `dropidle`, and vardiff improvements):
|
||||
|
||||
| Patch | Purpose |
|
||||
|-------|---------|
|
||||
@@ -44,6 +44,7 @@ Eight patches are applied to upstream commit `cfb0f83` (which itself includes th
|
||||
| `0006` | Expose raw reject count in pool stats |
|
||||
| `0007` | Replace hardcoded "ckpool" branding in the coinbase scriptSig with "kamado" (same 6 bytes, consensus-safe) |
|
||||
| `0008` | Log full PoW reproduction data (raw 80-byte header, coinbase, merkle branches) when a share sets a new best difficulty |
|
||||
| `0009` | Send `mining.set_difficulty` before the first `mining.notify`, resume a reconnecting miner's tuned difficulty, and grant its first job a difficulty grace window |
|
||||
|
||||
### Middleware API
|
||||
|
||||
@@ -94,6 +95,7 @@ The Svelte 5 dashboard connects via WebSocket for real-time push updates (no pol
|
||||
- **ckpool kill-on-failure** — When bitcoind becomes unreachable, kamado-api kills ckpool so miners can failover to another pool. ckpool restarts automatically when bitcoind recovers.
|
||||
- **Deferred RPC** — Dashboard RPCs wait until the ckpool notifier completes on tip change to prioritize system resources for block reconstruction and validation.
|
||||
- **Log cursor persistence** — The tailer's file offset is stored in SQLite so block-solve detection resumes correctly after restart.
|
||||
- **Clean restarts for miners** — Upstream ckpool sends a reconnecting miner its first job *before* telling it the difficulty, and resets every client to the pool's `startdiff`, discarding what vardiff had tuned. Both produce a burst of "Above target" rejects on every pool restart. Patch `0009` sends `mining.set_difficulty` first, seeds the starting difficulty from the worker's retained hashrate, and accepts shares for the already-in-flight job down to the pool minimum until the next `mining.notify`.
|
||||
- **Warm-up resilience** — The ckpool socket client handles transient EOFs during startup gracefully instead of crashing.
|
||||
|
||||
## API Reference
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
diff --git a/src/stratifier.c b/src/stratifier.c
|
||||
index a856b3eb..fcb9eb94 100644
|
||||
--- a/src/stratifier.c
|
||||
+++ b/src/stratifier.c
|
||||
@@ -5477,6 +5477,69 @@ static void update_solo_client(sdata_t *sdata, workbase_t *wb, const int64_t cli
|
||||
stratum_add_send(sdata, json_msg, client_id, SM_UPDATE);
|
||||
}
|
||||
|
||||
+/* Kamado (patches/0009): restore a reconnecting miner's difficulty and
|
||||
+ * grant the work it already holds a difficulty grace window.
|
||||
+ *
|
||||
+ * Upstream gives every new connection ckp->startdiff, so a pool restart
|
||||
+ * discards the difficulty vardiff had tuned each miner to and makes it
|
||||
+ * re-converge from the pool default. ckpool does persist per-worker
|
||||
+ * hashrate across restarts (read_userstats, decayed by the downtime), so
|
||||
+ * the starting difficulty can be derived from it with the same formula
|
||||
+ * and clamps add_submit's vardiff uses.
|
||||
+ *
|
||||
+ * The grace window covers work in flight. A miner may hold queued jobs,
|
||||
+ * and much firmware only applies a new difficulty on the following job,
|
||||
+ * so its first shares can be below the difficulty we just assigned.
|
||||
+ * Pointing diff_change_job_id past the current workbase makes the
|
||||
+ * existing check in parse_submit validate those against old_diff (the
|
||||
+ * pool minimum) instead of rejecting them as "high diff". The next
|
||||
+ * mining.notify closes the window, since by then the client has had the
|
||||
+ * difficulty for a whole job.
|
||||
+ *
|
||||
+ * Needs to be entered with no workbase or instance lock held. */
|
||||
+static void resume_client_diff(ckpool_t *ckp, sdata_t *sdata, stratum_instance_t *client)
|
||||
+{
|
||||
+ const worker_instance_t *worker = client->worker_instance;
|
||||
+ int64_t optimal, mindiff, network_diff = 0;
|
||||
+ double dsps = 0;
|
||||
+
|
||||
+ ck_rlock(&sdata->workbase_lock);
|
||||
+ if (sdata->current_workbase) {
|
||||
+ network_diff = sdata->current_workbase->network_diff;
|
||||
+ /* Every job sent so far, including the one about to go out,
|
||||
+ * predates this client acting on its difficulty. */
|
||||
+ client->diff_change_job_id = sdata->workbase_id + 1;
|
||||
+ }
|
||||
+ ck_runlock(&sdata->workbase_lock);
|
||||
+
|
||||
+ /* Read unlocked, as the vardiff path in add_submit does: these are
|
||||
+ * decaying averages used as a heuristic, not accounting. */
|
||||
+ if (worker)
|
||||
+ dsps = worker->dsps5 > 0 ? worker->dsps5 : worker->dsps1;
|
||||
+ if (dsps > 0) {
|
||||
+ optimal = lround(dsps * 3.33);
|
||||
+ /* Client suggest diff overrides worker mindiff */
|
||||
+ if (client->suggest_diff)
|
||||
+ mindiff = client->suggest_diff;
|
||||
+ else
|
||||
+ mindiff = worker->mindiff;
|
||||
+ optimal = MAX(optimal, ckp->mindiff);
|
||||
+ optimal = MAX(optimal, mindiff);
|
||||
+ if (ckp->maxdiff)
|
||||
+ optimal = MIN(optimal, ckp->maxdiff);
|
||||
+ if (network_diff > 0)
|
||||
+ optimal = MIN(optimal, network_diff);
|
||||
+ if (optimal > 0 && optimal != client->diff) {
|
||||
+ LOGNOTICE("Client %s resuming at diff %"PRId64" from retained hashrate instead of startdiff %"PRId64,
|
||||
+ client->identity, optimal, ckp->startdiff);
|
||||
+ client->diff = optimal;
|
||||
+ }
|
||||
+ }
|
||||
+ /* Floor for the grace window only. client->diff stays what we
|
||||
+ * advertise; vardiff overwrites old_diff on its first adjustment. */
|
||||
+ client->old_diff = ckp->mindiff > 0 ? ckp->mindiff : 1;
|
||||
+}
|
||||
+
|
||||
/* Needs to be entered with client holding a ref count. */
|
||||
static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_val,
|
||||
json_t **err_val)
|
||||
@@ -5558,6 +5621,19 @@ out:
|
||||
sdata_t *sdata = ckp->sdata;
|
||||
workbase_t *wb;
|
||||
|
||||
+ /* Kamado (patches/0009): pick up where vardiff left off and
|
||||
+ * arm the first-job grace window. Called before any lock is
|
||||
+ * taken here — it acquires the workbase lock itself. */
|
||||
+ resume_client_diff(ckp, sdata, client);
|
||||
+
|
||||
+ /* Kamado (patches/0009): mining.set_difficulty must precede
|
||||
+ * the first mining.notify. Upstream sent the job first, so a
|
||||
+ * miner started hashing it at whatever difficulty its firmware
|
||||
+ * defaults to and its early shares came back under the
|
||||
+ * difficulty we had assigned — a burst of "high diff" rejects
|
||||
+ * from every miner on every pool restart. */
|
||||
+ stratum_send_diff(sdata, client);
|
||||
+
|
||||
/* To avoid grabbing recursive lock */
|
||||
ck_wlock(&sdata->workbase_lock);
|
||||
wb = sdata->current_workbase;
|
||||
@@ -5573,8 +5649,6 @@ out:
|
||||
ck_wlock(&sdata->workbase_lock);
|
||||
wb->readcount--;
|
||||
ck_wunlock(&sdata->workbase_lock);
|
||||
-
|
||||
- stratum_send_diff(sdata, client);
|
||||
}
|
||||
return json_boolean(ret);
|
||||
}
|
||||
@@ -6567,9 +6641,12 @@ static void update_client(const stratum_instance_t *client, const int64_t client
|
||||
{
|
||||
sdata_t *sdata = client->sdata;
|
||||
|
||||
+ /* Kamado (patches/0009): difficulty before work, so a client never
|
||||
+ * starts a job without knowing the difficulty it will be judged
|
||||
+ * against. Upstream sent the update first. */
|
||||
+ stratum_send_diff(sdata, client);
|
||||
if (!client->ckp->btcsolo)
|
||||
stratum_send_update(sdata, client_id, true);
|
||||
- stratum_send_diff(sdata, client);
|
||||
}
|
||||
|
||||
static json_params_t
|
||||
Reference in new issue
Block a user