fix(stratum_v1): support float difficulty in mining.set_difficulty#24
fix(stratum_v1): support float difficulty in mining.set_difficulty#24average-gary wants to merge 7 commits into256foundation:mainfrom
Conversation
|
This issue was also reported, with a log of the Stratum v1 conversation, in #27. |
d92a7d4 to
dc89858
Compare
rkuester
left a comment
There was a problem hiding this comment.
Nice! Glad you submitted this. @pool2win and I considered implementing fractional difficulties in Mujina and Hydrapool, but weren't sure it was valid Stratum v1 (whatever that is!). Good to know some servers do implement it.
A few comments...
dc89858 to
1d16cb2
Compare
Add cases for whole and fractional f64 difficulties in the existing display test, with comments grouping them by behavior.
from_f64 truncates to integer arithmetic before dividing. For values >= 1.0, the fractional part is dropped (200.5 becomes 200). For sub-1.0 values, the reciprocal is truncated (0.003 becomes 1/333, round-tripping as 0.003003). This test asserts the correct behavior and is marked #[should_panic] to document the known bug while keeping CI green.
Implement Div<f64> for U256 by decomposing the float into its exact rational form (mantissa * 2^exponent) via num-traits' Float::integer_decode() and performing the division entirely in integer arithmetic, preserving all 53 bits of mantissa precision. For negative exponents (fractional divisors), a 512-bit intermediate avoids overflow when left-shifting the dividend. Bring in num-traits 0.2 for the float decomposition.
The previous implementation truncated the f64 to integer arithmetic before dividing. For values >= 1.0, the fractional part was dropped (200.5 became 200). For sub-1.0 values, the reciprocal was truncated (0.003 became 1/333, round-tripping as 0.003003). Delegate to U256's Div<f64>, which preserves all 53 bits of mantissa precision. Remove the #[should_panic] annotation from the preceding round-trip test, since it now passes.
Stratum v1 pools may send difficulty as either integer or float values (e.g., 0.001 for low-difficulty vardiff). The previous implementation used as_u64() which rejected float values with "difficulty not a number". Switch the DifficultyChanged event payload and ProtocolState.difficulty field from u64 to f64, and parse incoming difficulty with as_f64(). Construct the internal Difficulty type via Difficulty::from_f64(). Add a guard for non-finite values as defense-in-depth (JSON has no NaN/Infinity representation, so serde_json never produces them). Add tests for float difficulty parsing.
The SourceState.difficulty field was u64, which truncated fractional difficulties (e.g. 0.001 became 0). Change to f64 to preserve the value received from the pool.
The 256-bit target-to-difficulty conversion can introduce tiny arithmetic residuals in the low-order digits. Round to 12 significant digits, derived from the conversion's error bound with four orders of magnitude of margin.
7a66f83 to
c7f2495
Compare
rkuester
left a comment
There was a problem hiding this comment.
Looks good, thanks for making those changes! I reviewed them, and squashed them into your commit.
Then I realized Difficulty wasn't quite ready to handle from_f64() and as_f64() accurately, and added several commits shore that up.
Would you please take a quick look at my additions, and give this branch one more test, with a pool that actually sends fractional values, before I merge it?
In particular, |
Summary
Stratum v1 pools may send difficulty as either integer or float values (e.g.,
0.001for low-difficulty vardiff). The previous implementation usedas_u64()which rejected float values with the errordifficulty not a number.Problem
When connecting to pools or translators that use variable difficulty with sub-integer values, mujina would fail with:
This was encountered when testing with the SRI (Stratum Reference Implementation) translator which sends float difficulties for vardiff.
Changes
DifficultyChangedevent payload fromu64tof64handle_set_difficultyto parse withas_f64(), falling back toas_u64()for compatibilityProtocolState.difficultyfield toOption<f64>Difficulty::from_f64()instead ofDifficulty::from()when converting to internalDifficultytypeTesting
test_handle_set_difficulty_floatthat verifies float parsing