Skip to content

fix: don't feed external miners stale jobs during initial sync - #669

Merged
n13 merged 1 commit into
mainfrom
fix/no-stale-jobs-during-sync
Aug 14, 2026
Merged

fix: don't feed external miners stale jobs during initial sync#669
n13 merged 1 commit into
mainfrom
fix/no-stale-jobs-during-sync

Conversation

@n13

@n13 n13 commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Human overview

Fixes bug where miner runs at 100% capacity the entire time a node is syncing (on a stale / invalid job that's never canceled)

Error mode

A GUI/CLI miner shows a full, real hashrate while its node is still deep in major sync (e.g. block 51k of 878k). It is not a test mode — the miner is genuinely hashing, but on a job that can never produce a block:

  1. Startup window. The mining loop gates on is_major_syncing(), but at startup that is still false before the first peers connect, and the no-peers check had a 30-second grace period that let mining proceed. The loop therefore broadcast job 1 — built on whatever stale local best block the node had — one second after spawn (⛏️ Broadcasting job 1 right after ⛏️ QPoW Mining task spawned).
  2. No cancel. The miner protocol has only Ready / NewJob / JobResult. When sync engages the node just stops sending new jobs, so miners grind the last job indefinitely at full hashrate.
  3. Stale job served on connect. MinerServer stores the last broadcast job and hands it to every newly connecting miner; it was never cleared, so a miner connecting mid-sync immediately received the pre-sync job.

Every seal found this way is discarded (Received stale result from miner … ignoring, plus the seal re-verification under the build lock), so the only damage is hours of wasted work — but it makes syncing nodes look like they are mining.

Fix (minimal)

  • Pause immediately when offline instead of after a 30s grace period. The grace period is exactly what opened the startup window, and with external mining a pause is nearly free: connected miners keep their current job through a brief peer blip anyway, and a fresh job is broadcast on resume. This removes the offline_since / OFFLINE_GRACE_PERIOD machinery (net −16 lines).
  • Clear the stored job when pausing for major sync (MinerServer::clear_current_job), so miners connecting during a sync receive no work until the first post-sync broadcast.

Already-connected miners that hold a job when a sync begins still grind it until superseded — stopping them would need a cancel message in the wire protocol (ALPN bump), out of scope here.

Testing

  • New unit test clearing_the_current_job_stops_serving_it_to_new_miners.
  • cargo test -p quantus-node miner_server suite passes; cargo +nightly fmt --check clean.

At startup is_major_syncing() is still false and the offline check had a
30s grace period, so the mining loop broadcast a job built on the stale
local best block before sync engaged. The protocol has no cancel message
and the stored job was never cleared, so external miners — including ones
connecting mid-sync — ground that job at full hashrate for the entire
sync, with every result discarded as stale.

Pause mining immediately when the node has no peers (the grace period is
what opened the startup window; with external mining a pause is nearly
free), and clear the stored job when pausing for major sync so miners
connecting during the sync receive no work until a fresh post-sync job
is broadcast.

@n13 n13 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE (opinion; GitHub does not permit approving one's own PR).

No blocking findings.

The zero-peer gate now prevents the normal startup window from broadcasting a stale local candidate, and the major-sync path clears the stored server job once the worker invalidates the active build. I traced the worker-version transition through handle_external_mining, confirmed stale results remain rejected and seal submission remains re-verified under the build lock, and checked the documented limitation that the current wire protocol cannot cancel work already held by connected miners.

Validation:

  • cargo +nightly fmt --all -- --check — passed.
  • SKIP_WASM_BUILD=1 cargo test --locked -p quantus-node miner_server -- --nocapture — passed, 10 tests (including clearing_the_current_job_stops_serving_it_to_new_miners).
  • SKIP_WASM_BUILD=1 cargo clippy --locked -p quantus-node --all-targets --no-deps — passed; only existing unrelated workspace warnings were emitted.
  • git diff --check 37ac3db8...2908c749 — passed.
  • Live CI at 2908c74950c1e8e763161460dd71f75cefa85886 — format, Linux/macOS check-and-test matrix, and Clippy/doc jobs all successful.

@n13
n13 merged commit c3f7f3b into main Aug 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants