Skip to content

Commit 2323188

Browse files
chopperbrianoclaude
andcommitted
Dashboard: stop lying about RPC port and PSP registration (win.26)
Three fixes after a long debugging session showed the dashboard was displaying things it had no way to verify: - Payout address: read psp1payout (mctrivia is pool index 1; pool 0 is the local pool which has no payout). Falls back to psp0payout for legacy configs. - PSP status: dropped the "awaiting server registration" branch that was based on substring-searching map.json for the payout address. map.json contains only anonymized geo data ({version,country, region,city,lat,lon}) — no payout addresses or peer IDs — so the check could never match for anyone. Now just shows "Pool reachable" + node count, which is the strongest claim the public data supports. - RPC server: probe the listen socket via TCP connect (cached for 30s) instead of trusting AppMain's raw RPC::Server pointer. The pointer can outlive the actual socket if the detached accept-loop thread dies, which produced "RPC Server: Port 14024" lines for a service that was no longer listening. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eaa4fdb commit 2323188

3 files changed

Lines changed: 98 additions & 35 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ SET(PATCH_VERSION 0)
4040
SET(SO_VERSION 0)
4141

4242
# Windows port build number (increment for each Windows-specific release)
43-
SET(WIN_BUILD 25)
43+
SET(WIN_BUILD 26)
4444

4545
# Add source directory
4646
include_directories(src)

src/ConsoleDashboard.cpp

Lines changed: 87 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
#include <vector>
1818

1919
#ifdef _WIN32
20+
#include <winsock2.h>
21+
#include <ws2tcpip.h>
2022
#include <windows.h>
2123
#include <conio.h>
24+
#pragma comment(lib, "Ws2_32.lib")
2225
#endif
2326

2427
// ---- VT100 escape helpers --------------------------------------------------
@@ -361,11 +364,24 @@ void ConsoleDashboard::render() {
361364
bool dgbOnline = (dgb != nullptr);
362365
bool dbOnline = (db != nullptr);
363366
bool ipfsOnline = (app->getIPFSIfSet() != nullptr);
364-
RPC::Server* rpcServer = app->getRpcServerIfSet();
365-
bool rpcOnline = (rpcServer != nullptr);
366-
unsigned int rpcPort = 0;
367-
if (rpcOnline) {
368-
rpcPort = rpcServer->getPort();
367+
368+
// RPC: probe the actual listen socket. AppMain stores a raw pointer to
369+
// RPC::Server that becomes dangling if the detached accept-loop thread
370+
// dies, so trusting getRpcServerIfSet() can show a stale port for a
371+
// service that isn't actually listening.
372+
{
373+
auto rnow = std::chrono::steady_clock::now();
374+
auto rElapsed = std::chrono::duration<double>(rnow - _lastRpcProbe).count();
375+
if (rElapsed >= 30.0 || !_rpcProbed) {
376+
std::thread([this]() { probeRpcServer(); }).detach();
377+
}
378+
}
379+
bool rpcOnline;
380+
unsigned int rpcPort;
381+
{
382+
std::lock_guard<std::mutex> lock(_rpcProbeMutex);
383+
rpcOnline = _rpcListening;
384+
rpcPort = _rpcProbedPort;
369385
}
370386
WebServer* webServer = app->getWebServerIfSet();
371387
bool webOnline = (webServer != nullptr && webServer->isRunning());
@@ -644,17 +660,70 @@ void ConsoleDashboard::render() {
644660
std::cout << out.str() << std::flush;
645661
}
646662

663+
// ---- RPC server liveness probe ---------------------------------------------
664+
665+
void ConsoleDashboard::probeRpcServer() {
666+
// Read the configured port (default 14024 matches RPC::Server::Server)
667+
unsigned int port = 14024;
668+
try {
669+
Config config("config.cfg");
670+
port = (unsigned int)config.getInteger("rpcassetport", 14024);
671+
} catch (...) {}
672+
673+
bool listening = false;
674+
#ifdef _WIN32
675+
WSADATA wsa;
676+
if (WSAStartup(MAKEWORD(2, 2), &wsa) == 0) {
677+
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
678+
if (s != INVALID_SOCKET) {
679+
// Non-blocking + select-with-timeout: short, never hang the dashboard
680+
u_long nonblock = 1;
681+
ioctlsocket(s, FIONBIO, &nonblock);
682+
683+
sockaddr_in addr{};
684+
addr.sin_family = AF_INET;
685+
addr.sin_port = htons((u_short)port);
686+
addr.sin_addr.s_addr = htonl(0x7F000001); // 127.0.0.1
687+
688+
int rc = connect(s, (sockaddr*)&addr, sizeof(addr));
689+
if (rc == 0) {
690+
listening = true;
691+
} else if (WSAGetLastError() == WSAEWOULDBLOCK) {
692+
fd_set wfds;
693+
FD_ZERO(&wfds);
694+
FD_SET(s, &wfds);
695+
timeval tv{0, 200000}; // 200 ms
696+
int sel = select(0, nullptr, &wfds, nullptr, &tv);
697+
if (sel > 0) {
698+
int err = 0;
699+
int errlen = sizeof(err);
700+
getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &errlen);
701+
if (err == 0) listening = true;
702+
}
703+
}
704+
closesocket(s);
705+
}
706+
WSACleanup();
707+
}
708+
#endif
709+
710+
std::lock_guard<std::mutex> lock(_rpcProbeMutex);
711+
_rpcListening = listening;
712+
_rpcProbedPort = port;
713+
_rpcProbed = true;
714+
_lastRpcProbe = std::chrono::steady_clock::now();
715+
}
716+
647717
// ---- PSP registration status ------------------------------------------------
648718

649719
void ConsoleDashboard::checkPspRegistration() {
650720
auto now = std::chrono::steady_clock::now();
651721

652-
// Check pool reachability via map.json (public, no auth needed).
653-
// map.json is the authoritative list of nodes the server has recently
654-
// seen alive, so its contents are the only honest signal for "am I
655-
// registered". The /keepalive endpoint ALWAYS returns
656-
// {"error":"unsubscribe failed will time out anyways"} regardless of
657-
// success — don't use it as a registration signal.
722+
// map.json is anonymized geo data — entries are {version,country,region,city,
723+
// longitude,latitude} only, no payout addresses or peer IDs. So all we can
724+
// tell from it is "the pool server is up" and "how many nodes it sees online".
725+
// Anything stronger (am I registered, am I being paid) requires a per-node
726+
// endpoint mctrivia hasn't exposed.
658727
try {
659728
std::string mapResponse = CurlHandler::get("https://ipfs.digiassetx.com/map.json", 5000);
660729
int nodeCount = 0;
@@ -664,28 +733,7 @@ void ConsoleDashboard::checkPspRegistration() {
664733
pos++;
665734
}
666735
_pspNodeCount = nodeCount;
667-
668-
// Check if our own payout address appears anywhere in map.json —
669-
// that's a strong signal the server sees us as an active contributor.
670-
// (Peer ID matching would be better but map.json keys by payout.)
671-
bool selfListed = false;
672-
try {
673-
Config config("config.cfg");
674-
std::string payout = config.getString("psp0payout", "");
675-
if (!payout.empty() && mapResponse.find(payout) != std::string::npos) {
676-
selfListed = true;
677-
}
678-
} catch (...) {}
679-
680-
if (selfListed) {
681-
_pspStatus = "Pool reachable - this node is listed";
682-
} else {
683-
// Server-side registration is not instant. Once keepalives start
684-
// arriving, mctrivia's server has to crawl us, verify the pinned
685-
// content over libp2p, and refresh map.json on its own schedule.
686-
// In practice this takes a few hours after a brand-new subscribe.
687-
_pspStatus = "Pool reachable - awaiting server registration (can take a few hours)";
688-
}
736+
_pspStatus = "Pool reachable";
689737
_lastPspCheck = now; // cache for 10 min
690738
} catch (...) {
691739
_pspStatus = "Pool unreachable";
@@ -699,7 +747,12 @@ void ConsoleDashboard::loadPayoutInfo() {
699747
if (!_payoutLoaded) {
700748
try {
701749
Config config("config.cfg");
702-
_payoutAddress = config.getString("psp0payout", "");
750+
// mctrivia is pool index 1 (local is 0). Fall back to psp0payout
751+
// for legacy configs that wrote the wrong key.
752+
_payoutAddress = config.getString("psp1payout", "");
753+
if (_payoutAddress.empty()) {
754+
_payoutAddress = config.getString("psp0payout", "");
755+
}
703756
} catch (...) {}
704757
_payoutLoaded = true;
705758
_lastBalanceTime = std::chrono::steady_clock::now() - std::chrono::seconds(120); // force immediate fetch

src/ConsoleDashboard.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ class ConsoleDashboard {
9797
bool _portCheckDone = false;
9898
void checkPorts();
9999

100+
// RPC server liveness probe (cached). The AppMain raw pointer can outlive
101+
// the actual listen socket if the RPC accept loop dies in its detached
102+
// thread, so we verify by TCP-connecting to the configured rpcassetport.
103+
std::mutex _rpcProbeMutex;
104+
bool _rpcProbed = false;
105+
bool _rpcListening = false;
106+
unsigned int _rpcProbedPort = 0;
107+
std::chrono::steady_clock::time_point _lastRpcProbe;
108+
void probeRpcServer();
109+
100110
// IPFS announce detection/repair (for NAT'd users who have port forwarded
101111
// but Kubo's AutoNAT hasn't yet published a direct address in /id)
102112
std::mutex _ipfsAnnounceMutex;

0 commit comments

Comments
 (0)