upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-01 12:23:31 -0300
committerGitHub <noreply@github.com>2026-08-01 12:23:31 -0300
commit24ed292f0524f1bc77c317b60d7ecf265596b3dd (patch)
treeacdbccfca6bc0725751af8e653c16b7797db8a3f /src/net.cpp
parent64b9507846846e468ebba7e00fb59971edceca3b (diff)
diag: let the node say what is wrong with it (#81)
* Bump to 1.2.11 One change, and it is the reason a node on Windows kept collecting peers it could not hear. FD_SETSIZE was never defined in this tree, so it defaulted to 64 and select() could watch the listen socket plus 63 peers -- everything past that stayed connected and was never read again. Windows FD_SET stops adding once its array is full and says nothing, so there was no error to notice. Because new connections go on the end of vNodes, it was always the newest connection that starved, including the ones dialed to fix connectivity. Measured on two nodes: 277 and 90 CNode objects against 93 and 90 live sockets, 316 and 355 sockets in a bound-but-never-connected state, and a status bar reporting hundreds of peers for a node really talking to thirty. 1.2.11 raises FD_SETSIZE to 1024 on Windows, caps connections at 125, drops a node stuck undeliverable after 60 seconds instead of holding it forever, stops answering a failed select() with POSIX code that has no meaning on Windows, and warns when there are more peers than select can watch. * diag: let the node say what is wrong with it Every hard problem in this project so far was found from outside the node -- sockets read over WMI, committed memory compared against a README, a grep over somebody debug.log. In each case the node knew and had no way to say so. One report, rendered identically for the GUI panel and the log: peers held vs peers actually watched by select(), blocks received and how many arrived without a parent, proof-of-work mode with the live miner thread count, and per peer the age of the last message in each direction plus anything stuck unsent.
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 5ef8316..19893ee 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -46,6 +46,96 @@ CCriticalSection cs_mapRelay;
46map<CInv, int64> mapAlreadyAskedFor; 46map<CInv, int64> mapAlreadyAskedFor;
47string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) 47string strBtfConnect; // .btf peer to keep connected to (from /connectbtf)
48 48
49int nPeersWatched = 0;
50int64 nBlocksReceived = 0;
51int64 nBlocksWithoutParent = 0;
52static int64 nNodeStartTime = 0;
53
54// Defined further down, next to the counter it reads. The miner keeps its own
55// live count -- threads bump it on the way in and on the way out -- which is
56// the honest number here: how many are hashing now, not how many were asked to.
57static int MinersRunningCount();
58
59// Seconds rendered the way a person reads them off a screen.
60static string FormatAge(int64 nSeconds)
61{
62 if (nSeconds < 0) return "never";
63 if (nSeconds < 60) return strprintf("%llds", (long long)nSeconds);
64 if (nSeconds < 3600)
65 return strprintf("%lldm%02llds", (long long)(nSeconds / 60), (long long)(nSeconds % 60));
66 return strprintf("%lldh%02lldm", (long long)(nSeconds / 3600), (long long)((nSeconds % 3600) / 60));
67}
68
69string GetDiagnosticsText()
70{
71 int64 nNow = GetTime();
72 string str;
73
74 int nInbound = 0, nHeld = 0;
75 vector<CNode*> vCopy;
76 CRITICAL_BLOCK(cs_vNodes)
77 {
78 vCopy = vNodes;
79 nHeld = (int)vNodes.size();
80 foreach(CNode* pnode, vNodes)
81 if (pnode->fInbound)
82 nInbound++;
83 }
84
85 int nMedian = GetPeerMedianHeight();
86
87 str += "Bitflash node diagnostics\n";
88 str += strprintf(" uptime %s\n",
89 FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str());
90 if (nMedian < 0)
91 str += strprintf(" height %d (no peer has said where it is)\n", nBestHeight);
92 else
93 str += strprintf(" height %d (peers report %d, %s)\n",
94 nBestHeight, nMedian,
95 nBestHeight >= nMedian ? "level or ahead"
96 : strprintf("behind by %d", nMedian - nBestHeight).c_str());
97 str += strprintf(" peers held %d (%d inbound, %d outbound)\n",
98 nHeld, nInbound, nHeld - nInbound);
99
100 // The number that would have made the deafness obvious. Anything held but
101 // not watched is a socket this node will never read again.
102 str += strprintf(" peers watched %d of %d that select() can hold%s\n",
103 nPeersWatched, (int)FD_SETSIZE - 1,
104 nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : "");
105
106 if (nBlocksReceived > 0)
107 str += strprintf(" blocks received %lld (%lld arrived without a parent, %.1f%%)\n",
108 (long long)nBlocksReceived, (long long)nBlocksWithoutParent,
109 100.0 * nBlocksWithoutParent / nBlocksReceived);
110 else
111 str += " blocks received 0\n";
112
113 int nMining = MinersRunningCount();
114 str += strprintf(" proof of work %s mode%s\n",
115 RandomXFastReady() ? "fast (2 GB dataset)" : "light (256 MB cache)",
116 nMining > 0
117 ? strprintf(", mining on %d thread(s), about %d MB",
118 nMining,
119 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str()
120 : ", not mining");
121
122 str += "\n peer dir height last recv last send unsent\n";
123 foreach(CNode* pnode, vCopy)
124 {
125 int nSendSize = 0;
126 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
127 nSendSize = (int)pnode->vSend.size();
128 str += strprintf(" %-28s %-4s %6d %10s %10s %7d\n",
129 pnode->addr.ToString().substr(0, 28).c_str(),
130 pnode->fInbound ? "in" : "out",
131 pnode->nStartingHeight,
132 FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(),
133 FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(),
134 nSendSize);
135 }
136 return str;
137}
138
49 139
50bool GetMyExternalIP(unsigned int& ipRet) 140bool GetMyExternalIP(unsigned int& ipRet)
51{ 141{
@@ -1058,6 +1148,24 @@ void ThreadSocketHandler2(void* parg)
1058 (int)vNodes.size() - (int)FD_SETSIZE + 1); 1148 (int)vNodes.size() - (int)FD_SETSIZE + 1);
1059 } 1149 }
1060 } 1150 }
1151 nPeersWatched = (int)nWatched - 1; // less the listen socket
1152 }
1153
1154 // Say the state of the node out loud now and then, so a log pulled off
1155 // a machine three weeks later still answers "was it hearing anybody".
1156 {
1157 static int64 nLastReport = 0;
1158 // Wound forward on the first pass so the first report lands about a
1159 // minute in, once peers have had time to connect. A report written
1160 // at second zero says nothing, and waiting ten minutes for the
1161 // first one is ten minutes of a log that cannot answer anything.
1162 if (nLastReport == 0)
1163 nLastReport = GetTime() - 9 * 60;
1164 if (GetTime() - nLastReport > 10 * 60)
1165 {
1166 nLastReport = GetTime();
1167 printf("%s\n", GetDiagnosticsText().c_str());
1168 }
1061 } 1169 }
1062 1170
1063 vfThreadRunning[0] = false; 1171 vfThreadRunning[0] = false;
@@ -1432,6 +1540,14 @@ void ThreadMessageHandler2(void* parg)
1432static CCriticalSection cs_nMinersRunning; 1540static CCriticalSection cs_nMinersRunning;
1433static int nMinersRunning = 0; 1541static int nMinersRunning = 0;
1434 1542
1543static int MinersRunningCount()
1544{
1545 int n = 0;
1546 CRITICAL_BLOCK(cs_nMinersRunning)
1547 n = nMinersRunning;
1548 return n;
1549}
1550
1435void ThreadBitcoinMiner(void* parg) 1551void ThreadBitcoinMiner(void* parg)
1436{ 1552{
1437 int nThreadId = (int)(intptr_t)parg; 1553 int nThreadId = (int)(intptr_t)parg;
@@ -1490,6 +1606,7 @@ int StartMinerThreads()
1490bool StartNode(string& strError) 1606bool StartNode(string& strError)
1491{ 1607{
1492 strError = ""; 1608 strError = "";
1609 nNodeStartTime = GetTime();
1493 1610
1494 // Sockets startup 1611 // Sockets startup
1495 WSADATA wsadata; 1612 WSADATA wsadata;