Skip to content

channels_sv2: fix chain-tip transition correctness gaps in the job state machine - #2255

Merged
plebhash merged 4 commits into
stratum-mining:mainfrom
plebhash:2026-08-03-fix-chain-tip-transitions
Aug 7, 2026
Merged

channels_sv2: fix chain-tip transition correctness gaps in the job state machine#2255
plebhash merged 4 commits into
stratum-mining:mainfrom
plebhash:2026-08-03-fix-chain-tip-transitions

Conversation

@plebhash

@plebhash plebhash commented Aug 3, 2026

Copy link
Copy Markdown
Member

@plebhash
plebhash force-pushed the 2026-08-03-fix-chain-tip-transitions branch from b64326a to 2298b53 Compare August 7, 2026 00:11

@GitGab19 GitGab19 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACk

`JobStore::activate_future_job` returns `false` and no-ops when the
`template_id` matches no queued future job. The server-side `GroupChannel`
discarded that `bool`, unconditionally advanced its `chain_tip` and returned
`Ok(())`, despite its own doc comment promising an error.

The result was silent state corruption: subsequent group jobs were built
against a tip whose job had never been activated.

Propagate the activation result as `GroupChannelError::TemplateIdNotFound`,
before the chain tip is touched, so the error path leaves channel state
untouched. This mirrors what `server/standard.rs` and `server/extended.rs`
already do.
… update

`ExtendedChannel::on_chain_tip_update` is the JDC entry point for chain tip
updates. It cleared future jobs and rotated past jobs into stale, but never
transitioned `active_job`.

A share arriving in the window before the next `SetCustomMiningJobSuccess`
therefore passed the `is_active_job` check in `validate_share`, bypassed the
stale early-return, and was re-hashed against the NEW tip's `prev_hash` while
using the OLD job's coinbase and merkle path, yielding phantom `Valid` and
`BlockFound` results credited for work unrelated to the current tip.

Demote the previously active job into `past_jobs` before the past -> stale
rotation, so it ends up in `stale_jobs` and late shares for it are correctly
rejected as stale.
… a job

The client-side `GroupChannel` promoted the future job matching the
`SetNewPrevHash` `job_id` to `active_job` as-is, keeping `min_ntime` as
`None`. As a result `is_future()` kept returning `true` on the active job and
consumers dispatching on `min_ntime` misclassified it.

Call `set_no_future(set_new_prev_hash.min_ntime)` before promoting, as
`client/standard.rs` and `client/extended.rs` already do on activation.
Both client channels overwrote `active_job` with the activated future job.
The job that was active under the previous prev hash was never inserted into
`past_jobs`, so the past -> stale rotation right below could not see it: it
landed in neither `past_jobs` nor `stale_jobs` and was simply dropped.

As a result, a late share for the just-retired job fell through to the `else`
branch of `validate_share` and was rejected as `InvalidJobId` instead of
`Stale`. The server side already gets this right, since
`JobStore::activate_future_job` moves the current active job into `past_jobs`
before activating and then marks past jobs as stale.

Capture the previous job via `Option::replace` and insert it into `past_jobs`.
This happens after the `JobIdNotFound` early-return, so a failed activation
does not corrupt channel state.
@plebhash
plebhash force-pushed the 2026-08-03-fix-chain-tip-transitions branch from 2298b53 to a302aee Compare August 7, 2026 15:41
@plebhash
plebhash merged commit a11bf7a into stratum-mining:main Aug 7, 2026
14 checks passed
@plebhash
plebhash deleted the 2026-08-03-fix-chain-tip-transitions branch August 7, 2026 16:15
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.

channels_sv2: need to fix chain-tip transition correctness gaps in the job state machine

2 participants