upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 23e25ef..df7ca05 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -2596,6 +2596,18 @@ static void LocalAcceptFn(void*)
2596 break; 2596 break;
2597 continue; 2597 continue;
2598 } 2598 }
2599 // #6: let the OS probe a peer that has gone silent, so a half-open
2600 // session eventually errors out of recv and lands in the event
2601 // loop's existing reap path instead of sitting in gMiners forever.
2602 // This is a backstop, not the mechanism: the default cadence before
2603 // the first probe is hours on most stacks, so the idle timeout in
2604 // PoolEventLoop fires long before keepalive does. Miners arriving
2605 // via a relay ride a loopback socket pair whose remote transport is
2606 // the rendezvous socket owned by the tunnel pump threads; keepalive
2607 // cannot reach across that, which is part of why the idle timeout
2608 // exists.
2609 int keepalive = 1;
2610 setsockopt(c, SOL_SOCKET, SO_KEEPALIVE, (char*)&keepalive, sizeof(keepalive));
2599 LogPrint("pool", "[pool] miner arrived via loopback -- pushing to event loop\n"); 2611 LogPrint("pool", "[pool] miner arrived via loopback -- pushing to event loop\n");
2600 PushReady((btf_socket_t)c); 2612 PushReady((btf_socket_t)c);
2601 } 2613 }
@@ -2639,6 +2651,16 @@ static const int64 POOL_ACTIVE_WINDOW = 300; // a miner is "active" if it
2639static const int64 POOL_UNAUTH_GRACE_SECS = 60; // a connection still not 2651static const int64 POOL_UNAUTH_GRACE_SECS = 60; // a connection still not
2640 // authorized after this long is 2652 // authorized after this long is
2641 // idle, not merely starting up 2653 // idle, not merely starting up
2654// #6: a Miner used to leave gMiners only when RecvLine reported a clean
2655// disconnect. A half-open session (miner host died, NAT state expired,
2656// cable pulled) sends no FIN and reports no error, so it sat in the table
2657// forever -- authorize outpaced disconnect 2:1 on the live pool, and the
2658// table grew toward MAX_POOL_MINERS, past which real miners are refused.
2659// lastSeen is stamped on every complete line received; a miner that has
2660// said nothing for this long is gone in every way that matters. Ten
2661// minutes is far beyond the vardiff target (a share every 15s) and the
2662// stall watchdog (45s), so no miner that is actually hashing comes close.
2663static const int64 POOL_IDLE_TIMEOUT_SECS = 600;
2642 2664
2643// Non-blocking recv: pull whatever bytes are available now into buf, then 2665// Non-blocking recv: pull whatever bytes are available now into buf, then
2644// return one complete line if one is ready. Three outcomes: 2666// return one complete line if one is ready. Three outcomes:
@@ -4940,6 +4962,35 @@ static void PoolEventLoop()
4940 m->hashRate *= pow(0.5, (silent - grace) / expected); 4962 m->hashRate *= pow(0.5, (silent - grace) / expected);
4941 } 4963 }
4942 4964
4965 // 3.7. Idle reap (#6). Mirror the clean-disconnect path exactly --
4966 // log, close, erase from gMiners via std::find, delete -- just with
4967 // a different reason. Shares the miner already earned stay in
4968 // roundShareCount, exactly as they do when a miner cleanly
4969 // disconnects mid-round. connectTime stands in for lastSeen on a
4970 // connection that never completed a line. Erasing inside the loop
4971 // above would invalidate the iteration, so collect then reap.
4972 {
4973 int64 now = GetTime();
4974 std::vector<Miner*> idle;
4975 for (Miner* m : gMiners) {
4976 int64 last = (m->lastSeen > 0) ? m->lastSeen : m->connectTime;
4977 if (now - last > POOL_IDLE_TIMEOUT_SECS)
4978 idle.push_back(m);
4979 }
4980 for (Miner* m : idle) {
4981 LogPrint("worker", "[pool] dropping idle miner %s -- nothing"
4982 " received in over %llds (session shares: %llu,"
4983 " round shares: %llu)\n",
4984 m->worker.empty() ? "(never authorized)" : m->worker.c_str(),
4985 (long long)POOL_IDLE_TIMEOUT_SECS,
4986 (unsigned long long)m->sessionShares,
4987 (unsigned long long)m->roundShares);
4988 sock_close(m->fd);
4989 gMiners.erase(std::find(gMiners.begin(), gMiners.end(), m));
4990 delete m;
4991 }
4992 }
4993
4943 // 4. Select over miner sockets 4994 // 4. Select over miner sockets
4944 if (!gMiners.empty()) { 4995 if (!gMiners.empty()) {
4945 fd_set fds; FD_ZERO(&fds); 4996 fd_set fds; FD_ZERO(&fds);
@@ -4962,6 +5013,11 @@ static void PoolEventLoop()
4962 // terminate the whole node -- not just the pool. Every 5013 // terminate the whole node -- not just the pool. Every
4963 // line arrives from an unauthenticated socket. 5014 // line arrives from an unauthenticated socket.
4964 while (RecvLine(m->fd, m->readBuf, line, disc)) { 5015 while (RecvLine(m->fd, m->readBuf, line, disc)) {
5016 // Any complete line is proof of life (#6): authorize
5017 // and valid shares already stamp lastSeen themselves,
5018 // but a miner that only ever subscribes (or sends
5019 // unrecognised methods) must count as alive too.
5020 m->lastSeen = GetTime();
4965 try { 5021 try {
4966 HandleLine(m, line, roundShareCount, 5022 HandleLine(m, line, roundShareCount,
4967 roundShareTotal, blocksFoundThisSession); 5023 roundShareTotal, blocksFoundThisSession);