Skip to content

channels_sv2: clamp target to max_target instead of erroring#2118

Open
vnprc wants to merge 1 commit intostratum-mining:mainfrom
vnprc:channels-sv2/clamp-max-target
Open

channels_sv2: clamp target to max_target instead of erroring#2118
vnprc wants to merge 1 commit intostratum-mining:mainfrom
vnprc:channels-sv2/clamp-max-target

Conversation

@vnprc
Copy link
Contributor

@vnprc vnprc commented Mar 16, 2026

Problem

When a pool's vardiff computes a target easier than the client's declared
max_target, update_channel() returns Err(RequestedMaxTargetOutOfRange).
This silently breaks vardiff for the affected channel: the error is swallowed by
the caller, no difficulty update is applied, and the miner is permanently stuck
at its last successfully-set difficulty. The pool logs a WARN roughly every
60 seconds per affected channel while mining continues at the wrong difficulty.

This is not a hypothetical edge case. It triggers reliably for low-hashrate
miners whose natural vardiff target overshoots their declared max_target.

Root Cause

The max_target field in OpenExtendedMiningChannel means "the easiest
difficulty I will accept."
When vardiff computes a target easier than
max_target, the correct response is to assign exactly max_target — the
client has already declared that difficulty acceptable. The current code errors
instead, which is both incorrect per the spec and operationally harmful.

Fix

Replace the RequestedMaxTargetOutOfRange error path with a clamp in four
places:

  • ExtendedChannel::new() — channel creation
  • ExtendedChannel::update_channel() — vardiff updates
  • StandardChannel::new() — channel creation
  • StandardChannel::update_channel() — vardiff updates

Also removes two stray println! debug statements from ExtendedChannel::new().

Tests

The existing test_update_channel tests in both extended.rs and standard.rs
already exercise this path (very_small_hashrate = 0.1). They previously
asserted is_err() / RequestedMaxTargetOutOfRange. They now assert is_ok()
and verify the channel target was clamped to not_so_permissive_max_target.
All 50 unit tests pass.

Notes

  • RequestedMaxTargetOutOfRange is preserved in both error enums — it remains
    reachable from the UpdateChannel message handler path where the client itself
    sends a bad max_target.
  • No API surface changes (no new public types, no signature changes).

Pre-existing CI failure

cargo clippy currently fails on upstream main due to needless_lifetimes in
binary_sv2 — unrelated to this PR.

When a pool's vardiff computes a target easier than the client's declared
max_target, update_channel() returned Err(RequestedMaxTargetOutOfRange).
This silently broke vardiff: once triggered, no further difficulty
adjustments succeeded for that channel. The pool logged a WARN every
~60s per affected channel while the miner stayed stuck at its last
successfully-set difficulty.

The max_target field in OpenExtendedMiningChannel means "the easiest
difficulty I will accept." If the pool's calculation produces something
easier, the correct response is to assign exactly max_target — not to
error. The client explicitly declared this difficulty acceptable.

Apply the same clamping logic to both ExtendedChannel and StandardChannel,
in both new() (channel creation) and update_channel() (vardiff updates).
Update the corresponding tests to assert clamping behavior rather than
expecting an error. Also remove two stray println! debug statements from
ExtendedChannel::new().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant