upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rpc.cpp40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index f1a7153..23e25ef 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -2484,6 +2484,26 @@ static const double VARDIFF_MAX = 1e12;
2484static const int64 VARDIFF_STALL_SECS = 45; // 3x share target -- reacts faster without false-triggering on normal variance 2484static const int64 VARDIFF_STALL_SECS = 45; // 3x share target -- reacts faster without false-triggering on normal variance
2485static const double VARDIFF_STALL_CUT = 4.0; // divide difficulty by this much 2485static const double VARDIFF_STALL_CUT = 4.0; // divide difficulty by this much
2486 2486
2487// #7: m->hashRate is an EWMA sampled only when a share arrives, so a miner
2488// that stops hashing keeps its last estimate forever and RefreshStats sums
2489// it into the announced pool total for as long as the connection sits open
2490// -- the published pool hashrate ran several times over ground truth
2491// because of exactly this. The decay pass in PoolEventLoop applies this
2492// policy: a miner gets a grace of HASHRATE_IDLE_MISSES expected share
2493// intervals (expected interval = expected hashes per share at its current
2494// difficulty / its last rate estimate) with no share, then its estimate
2495// decays with a half-life of one more such interval, computed from
2496// absolute time so the result does not depend on how often the loop ticks.
2497// The interval is clamped to [HASHRATE_MIN_INTERVAL, HASHRATE_MAX_INTERVAL]
2498// so a fast miner at the vardiff floor (expected interval of milliseconds)
2499// is not punished for ordinary share-delivery jitter, and a very slow
2500// miner's grace does not stretch past the point where its estimate means
2501// anything. At the vardiff target of one share per 15s, a stopped miner's
2502// announced rate is gone within minutes.
2503static const double HASHRATE_IDLE_MISSES = 4.0; // grace, in expected share intervals
2504static const double HASHRATE_MIN_INTERVAL = 1.0; // seconds; floor for the expected interval
2505static const double HASHRATE_MAX_INTERVAL = 3600.0; // seconds; ceiling for the expected interval
2506
2487struct Miner { 2507struct Miner {
2488 btf_socket_t fd; 2508 btf_socket_t fd;
2489 bool authorised = false; 2509 bool authorised = false;
@@ -4900,6 +4920,26 @@ static void PoolEventLoop()
4900 } 4920 }
4901 } 4921 }
4902 4922
4923 // 3.6. Hashrate decay (#7) -- see the policy comment at
4924 // HASHRATE_IDLE_MISSES. The EWMA is only re-sampled on share arrival,
4925 // so without this pass a miner that stops hashing keeps its last
4926 // estimate forever and the summed pool hashrate RefreshStats (and
4927 // the nostr announcement built from it) publishes is fiction.
4928 for (Miner* m : gMiners) {
4929 if (m->hashRate <= 0.0 || m->lastShareTime <= 0) continue;
4930 double expected = ExpectedHashesForDifficulty(m->difficulty) /
4931 std::max(m->hashRate, 1e-9);
4932 if (expected < HASHRATE_MIN_INTERVAL) expected = HASHRATE_MIN_INTERVAL;
4933 if (expected > HASHRATE_MAX_INTERVAL) expected = HASHRATE_MAX_INTERVAL;
4934 double silent = (double)(GetTime() - m->lastShareTime);
4935 double grace = HASHRATE_IDLE_MISSES * expected;
4936 if (silent <= grace) continue;
4937 // Half-life of one expected interval past the grace period.
4938 // Written off absolute time, not per-tick, so the decay curve is
4939 // the same however often this loop runs.
4940 m->hashRate *= pow(0.5, (silent - grace) / expected);
4941 }
4942
4903 // 4. Select over miner sockets 4943 // 4. Select over miner sockets
4904 if (!gMiners.empty()) { 4944 if (!gMiners.empty()) {
4905 fd_set fds; FD_ZERO(&fds); 4945 fd_set fds; FD_ZERO(&fds);