diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 2039 |
1 files changed, 2039 insertions, 0 deletions
diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 0000000..449d91c --- /dev/null +++ b/src/net.cpp | |||
| @@ -0,0 +1,2039 @@ | |||
| 1 | // Copyright (c) 2009 Satoshi Nakamoto | ||
| 2 | // Distributed under the MIT/X11 software license, see the accompanying | ||
| 3 | // file license.txt or http://www.opensource.org/licenses/mit-license.php. | ||
| 4 | |||
| 5 | #pragma push_macro("snprintf") | ||
| 6 | #undef snprintf | ||
| 7 | #include <nlohmann/json.hpp> | ||
| 8 | #pragma pop_macro("snprintf") | ||
| 9 | #ifdef snprintf | ||
| 10 | #undef snprintf | ||
| 11 | #endif | ||
| 12 | #include "headers.h" | ||
| 13 | #include <thread> // hardware_concurrency, for the miner thread count | ||
| 14 | #ifdef _WIN32 | ||
| 15 | #include <winsock2.h> | ||
| 16 | #endif | ||
| 17 | #include <openssl/rand.h> | ||
| 18 | #include "btfaddr.h" | ||
| 19 | #include "btftunnel.h" | ||
| 20 | |||
| 21 | void ThreadReconnectCachedBtfPeers(void* parg); | ||
| 22 | void ThreadMessageHandler2(void* parg); | ||
| 23 | void ThreadSocketHandler2(void* parg); | ||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | // | ||
| 31 | // Global state variables | ||
| 32 | // | ||
| 33 | bool fClient = false; | ||
| 34 | uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); | ||
| 35 | CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); | ||
| 36 | unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) | ||
| 37 | CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); | ||
| 38 | CNode* pnodeLocalHost = &nodeLocalHost; | ||
| 39 | bool fShutdown = false; | ||
| 40 | array<bool, 10> vfThreadRunning; | ||
| 41 | vector<CNode*> vNodes; | ||
| 42 | CCriticalSection cs_vNodes; | ||
| 43 | map<CInv, CDataStream> mapRelay; | ||
| 44 | deque<pair<int64, CInv> > vRelayExpiration; | ||
| 45 | CCriticalSection cs_mapRelay; | ||
| 46 | map<CInv, int64> mapAlreadyAskedFor; | ||
| 47 | string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) | ||
| 48 | |||
| 49 | int nPeersWatched = 0; | ||
| 50 | int64 nBlocksReceived = 0; | ||
| 51 | int64 nBlocksWithoutParent = 0; | ||
| 52 | static int64 nNodeStartTime = 0; | ||
| 53 | |||
| 54 | static CCriticalSection cs_btfChurn; | ||
| 55 | static int64 nBtfResolveAttempts = 0; | ||
| 56 | static int64 nBtfResolveOk = 0; | ||
| 57 | static int64 nBtfResolveMiss = 0; | ||
| 58 | static int64 nBtfDialAttempts = 0; | ||
| 59 | static int64 nBtfDialOk = 0; | ||
| 60 | static int64 nBtfDialFailed = 0; | ||
| 61 | static int64 nBtfRegisterOk = 0; | ||
| 62 | static int64 nBtfRegisterFailed = 0; | ||
| 63 | static int64 nBtfPairOk = 0; | ||
| 64 | static int64 nBtfPairFailed = 0; | ||
| 65 | static int64 nBtfHandshakeNoRecv = 0; | ||
| 66 | static int64 nBtfHandshakeNoSend = 0; | ||
| 67 | static int64 nBtfHandshakeSilent = 0; | ||
| 68 | static string strBtfLastDialFail; | ||
| 69 | static string strBtfLastHandshakeTimeout; | ||
| 70 | |||
| 71 | void BtfChurnNoteResolveAttempt() | ||
| 72 | { | ||
| 73 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 74 | nBtfResolveAttempts++; | ||
| 75 | } | ||
| 76 | |||
| 77 | void BtfChurnNoteResolveResult(bool fOk) | ||
| 78 | { | ||
| 79 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 80 | { | ||
| 81 | if (fOk) nBtfResolveOk++; | ||
| 82 | else nBtfResolveMiss++; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting) | ||
| 87 | { | ||
| 88 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 89 | nBtfDialAttempts++; | ||
| 90 | LogPrint("net", "btfchurn: dial attempt addr=%s meeting=%s\n", | ||
| 91 | strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 92 | } | ||
| 93 | |||
| 94 | void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk) | ||
| 95 | { | ||
| 96 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 97 | { | ||
| 98 | if (fOk) | ||
| 99 | nBtfDialOk++; | ||
| 100 | else | ||
| 101 | { | ||
| 102 | nBtfDialFailed++; | ||
| 103 | strBtfLastDialFail = strBtfAddr + " via " + strMeeting; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | LogPrint("net", "btfchurn: dial %s addr=%s meeting=%s\n", | ||
| 107 | fOk ? "ok" : "failed", strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 108 | } | ||
| 109 | |||
| 110 | void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk) | ||
| 111 | { | ||
| 112 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 113 | { | ||
| 114 | if (fOk) nBtfRegisterOk++; | ||
| 115 | else nBtfRegisterFailed++; | ||
| 116 | } | ||
| 117 | LogPrint("net", "btfchurn: register %s meeting=%s\n", | ||
| 118 | fOk ? "ok" : "failed", strMeeting.c_str()); | ||
| 119 | } | ||
| 120 | |||
| 121 | void BtfChurnNotePairResult(const string& strMeeting, bool fOk) | ||
| 122 | { | ||
| 123 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 124 | { | ||
| 125 | if (fOk) nBtfPairOk++; | ||
| 126 | else nBtfPairFailed++; | ||
| 127 | } | ||
| 128 | LogPrint("net", "btfchurn: pair %s meeting=%s\n", | ||
| 129 | fOk ? "ok" : "timeout-or-drop", strMeeting.c_str()); | ||
| 130 | } | ||
| 131 | |||
| 132 | void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting, | ||
| 133 | bool fRecv, bool fSend) | ||
| 134 | { | ||
| 135 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 136 | { | ||
| 137 | if (!fRecv && !fSend) | ||
| 138 | nBtfHandshakeSilent++; | ||
| 139 | else | ||
| 140 | { | ||
| 141 | if (!fRecv) nBtfHandshakeNoRecv++; | ||
| 142 | if (!fSend) nBtfHandshakeNoSend++; | ||
| 143 | } | ||
| 144 | strBtfLastHandshakeTimeout = strprintf("%s via %s recv=%d send=%d", | ||
| 145 | strBtfAddr.empty() ? "unknown" : strBtfAddr.c_str(), | ||
| 146 | strMeeting.empty() ? "unknown" : strMeeting.c_str(), | ||
| 147 | fRecv, fSend); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | // --- Socket accounting (implementation lives in sockcount.h) -------------- | ||
| 152 | static const char* pszSockSite[SOCK_SITES] = { | ||
| 153 | "external-ip probe", "listen socket", "inbound accept", | ||
| 154 | "rendezvous dial", "rendezvous listen", "rendezvous accept", | ||
| 155 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", | ||
| 156 | "nostr relay" | ||
| 157 | }; | ||
| 158 | |||
| 159 | static string SockAccountingText() | ||
| 160 | { | ||
| 161 | long long nOpened[SOCK_SITES], nClosed[SOCK_SITES], nFailed[SOCK_SITES]; | ||
| 162 | long long nCollided[SOCK_SITES], nUntagged = 0; | ||
| 163 | BtfSockSnapshot(nOpened, nClosed, nFailed, nCollided, &nUntagged); | ||
| 164 | |||
| 165 | long long nUnconn[SOCK_SITES]; | ||
| 166 | BtfSockLiveUnconnected(nUnconn); | ||
| 167 | |||
| 168 | string str = "\n sockets by where they were created\n"; | ||
| 169 | str += " site opened closed live close failed live unconn\n"; | ||
| 170 | long long nTotalOpened = 0, nTotalClosed = 0, nTotalFailed = 0, nTotalUnconn = 0; | ||
| 171 | for (int i = 0; i < SOCK_SITES; i++) | ||
| 172 | { | ||
| 173 | nTotalOpened += nOpened[i]; | ||
| 174 | nTotalClosed += nClosed[i]; | ||
| 175 | nTotalFailed += nFailed[i]; | ||
| 176 | nTotalUnconn += nUnconn[i]; | ||
| 177 | if (nOpened[i] == 0) | ||
| 178 | continue; | ||
| 179 | str += strprintf(" %-24s %8lld %8lld %8lld %13lld %12lld\n", pszSockSite[i], | ||
| 180 | nOpened[i], nClosed[i], nOpened[i] - nClosed[i], nFailed[i], nUnconn[i]); | ||
| 181 | } | ||
| 182 | str += strprintf(" %-24s %8s %8lld %8s %13s %12s\n", "closed but never tagged", | ||
| 183 | "-", nUntagged, "-", "-", "-"); | ||
| 184 | str += strprintf(" %-24s %8lld %8lld %8lld %13lld %12lld\n", "TOTAL", | ||
| 185 | nTotalOpened, nTotalClosed, nTotalOpened - nTotalClosed, | ||
| 186 | nTotalFailed, nTotalUnconn); | ||
| 187 | |||
| 188 | // The live total is what this program believes it is holding. Comparing it | ||
| 189 | // against `Get-NetTCPConnection | ? OwningProcess -eq <pid>` needs the note | ||
| 190 | // in sockcount.h first: that table lists a wildcard-bound socket twice, so | ||
| 191 | // a raw row count always looks larger than this. Count orphans, not rows. | ||
| 192 | // | ||
| 193 | // "live unconn" is a getpeername on every tagged socket. Anything above 1 | ||
| 194 | // -- the listening socket -- means this program is holding a socket it | ||
| 195 | // never connected, which is the shape the leak hunt was looking for and | ||
| 196 | // never found. | ||
| 197 | if (nTotalFailed > 0) | ||
| 198 | { | ||
| 199 | str += " close failures by code\n"; | ||
| 200 | for (int i = 0; i < SOCK_SITES; i++) | ||
| 201 | { | ||
| 202 | if (nFailed[i] == 0) | ||
| 203 | continue; | ||
| 204 | map<int, long long> mapErr; | ||
| 205 | BtfSockCloseErrors(i, mapErr); | ||
| 206 | string strCodes; | ||
| 207 | for (map<int, long long>::const_iterator mi = mapErr.begin(); mi != mapErr.end(); ++mi) | ||
| 208 | strCodes += strprintf("%s%d x%lld", strCodes.empty() ? "" : ", ", | ||
| 209 | mi->first, mi->second); | ||
| 210 | str += strprintf(" %-24s %s\n", pszSockSite[i], strCodes.c_str()); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | // A handle number handed out again while a site still claimed it. Windows | ||
| 215 | // only reuses a number after the last close, so this says that site's | ||
| 216 | // bookkeeping outlived its socket -- and it will close that number again, | ||
| 217 | // on somebody else's connection. Any value above zero is a bug with a name | ||
| 218 | // attached. | ||
| 219 | for (int i = 0; i < SOCK_SITES; i++) | ||
| 220 | { | ||
| 221 | if (nCollided[i] == 0) | ||
| 222 | continue; | ||
| 223 | str += strprintf(" handle reused while %s still claimed it: %lld\n", | ||
| 224 | pszSockSite[i], nCollided[i]); | ||
| 225 | } | ||
| 226 | return str; | ||
| 227 | } | ||
| 228 | |||
| 229 | // Defined further down, next to the counter it reads. The miner keeps its own | ||
| 230 | // live count -- threads bump it on the way in and on the way out -- which is | ||
| 231 | // the honest number here: how many are hashing now, not how many were asked to. | ||
| 232 | static int MinersRunningCount(); | ||
| 233 | |||
| 234 | // Seconds rendered the way a person reads them off a screen. | ||
| 235 | static string FormatAge(int64 nSeconds) | ||
| 236 | { | ||
| 237 | if (nSeconds < 0) return "never"; | ||
| 238 | if (nSeconds < 60) return strprintf("%llds", (long long)nSeconds); | ||
| 239 | if (nSeconds < 3600) | ||
| 240 | return strprintf("%lldm%02llds", (long long)(nSeconds / 60), (long long)(nSeconds % 60)); | ||
| 241 | return strprintf("%lldh%02lldm", (long long)(nSeconds / 3600), (long long)((nSeconds % 3600) / 60)); | ||
| 242 | } | ||
| 243 | |||
| 244 | string GetDiagnosticsText() | ||
| 245 | { | ||
| 246 | int64 nNow = GetTime(); | ||
| 247 | string str; | ||
| 248 | |||
| 249 | int nInbound = 0, nHeld = 0; | ||
| 250 | vector<CNode*> vCopy; | ||
| 251 | CRITICAL_BLOCK(cs_vNodes) | ||
| 252 | { | ||
| 253 | vCopy = vNodes; | ||
| 254 | nHeld = (int)vNodes.size(); | ||
| 255 | foreach(CNode* pnode, vNodes) | ||
| 256 | if (pnode->fInbound) | ||
| 257 | nInbound++; | ||
| 258 | } | ||
| 259 | |||
| 260 | int nMedian = GetPeerMedianHeight(); | ||
| 261 | |||
| 262 | str += "Bitflash node diagnostics\n"; | ||
| 263 | str += strprintf(" uptime %s\n", | ||
| 264 | FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str()); | ||
| 265 | if (nMedian < 0) | ||
| 266 | str += strprintf(" height %d (no peer has said where it is)\n", nBestHeight); | ||
| 267 | else | ||
| 268 | str += strprintf(" height %d (peers report %d, %s)\n", | ||
| 269 | nBestHeight, nMedian, | ||
| 270 | nBestHeight >= nMedian ? "level or ahead" | ||
| 271 | : strprintf("behind by %d", nMedian - nBestHeight).c_str()); | ||
| 272 | str += strprintf(" peers held %d (%d inbound, %d outbound)\n", | ||
| 273 | nHeld, nInbound, nHeld - nInbound); | ||
| 274 | |||
| 275 | // The number that would have made the deafness obvious. Anything held but | ||
| 276 | // not watched is a socket this node will never read again. | ||
| 277 | str += strprintf(" peers watched %d of %d that select() can hold%s\n", | ||
| 278 | nPeersWatched, (int)FD_SETSIZE - 1, | ||
| 279 | nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); | ||
| 280 | |||
| 281 | if (nBlocksReceived > 0) | ||
| 282 | str += strprintf(" blocks received %lld (%lld arrived without a parent, %.1f%%)\n", | ||
| 283 | (long long)nBlocksReceived, (long long)nBlocksWithoutParent, | ||
| 284 | 100.0 * nBlocksWithoutParent / nBlocksReceived); | ||
| 285 | else | ||
| 286 | str += " blocks received 0\n"; | ||
| 287 | |||
| 288 | int nMining = MinersRunningCount(); | ||
| 289 | str += strprintf(" proof of work %s mode%s\n", | ||
| 290 | RandomXFastReady() ? "fast (2 GB dataset)" : "light (256 MB cache)", | ||
| 291 | nMining > 0 | ||
| 292 | ? strprintf(", mining on %d thread(s), about %d MB", | ||
| 293 | nMining, | ||
| 294 | (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() | ||
| 295 | : ", not mining"); | ||
| 296 | |||
| 297 | str += SockAccountingText(); | ||
| 298 | |||
| 299 | int64 nResolveAttempts, nResolveOk, nResolveMiss; | ||
| 300 | int64 nDialAttempts, nDialOk, nDialFailed; | ||
| 301 | int64 nRegisterOk, nRegisterFailed, nPairOk, nPairFailed; | ||
| 302 | int64 nHandshakeNoRecv, nHandshakeNoSend, nHandshakeSilent; | ||
| 303 | string strLastDialFail, strLastHandshakeTimeout; | ||
| 304 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 305 | { | ||
| 306 | nResolveAttempts = nBtfResolveAttempts; | ||
| 307 | nResolveOk = nBtfResolveOk; | ||
| 308 | nResolveMiss = nBtfResolveMiss; | ||
| 309 | nDialAttempts = nBtfDialAttempts; | ||
| 310 | nDialOk = nBtfDialOk; | ||
| 311 | nDialFailed = nBtfDialFailed; | ||
| 312 | nRegisterOk = nBtfRegisterOk; | ||
| 313 | nRegisterFailed = nBtfRegisterFailed; | ||
| 314 | nPairOk = nBtfPairOk; | ||
| 315 | nPairFailed = nBtfPairFailed; | ||
| 316 | nHandshakeNoRecv = nBtfHandshakeNoRecv; | ||
| 317 | nHandshakeNoSend = nBtfHandshakeNoSend; | ||
| 318 | nHandshakeSilent = nBtfHandshakeSilent; | ||
| 319 | strLastDialFail = strBtfLastDialFail; | ||
| 320 | strLastHandshakeTimeout = strBtfLastHandshakeTimeout; | ||
| 321 | } | ||
| 322 | str += "\n .btf churn\n"; | ||
| 323 | str += strprintf(" resolves %lld attempts, %lld ok, %lld no descriptor\n", | ||
| 324 | (long long)nResolveAttempts, (long long)nResolveOk, | ||
| 325 | (long long)nResolveMiss); | ||
| 326 | str += strprintf(" outbound dials %lld attempts, %lld ok, %lld failed\n", | ||
| 327 | (long long)nDialAttempts, (long long)nDialOk, | ||
| 328 | (long long)nDialFailed); | ||
| 329 | str += strprintf(" rendezvous local register ok/fail %lld/%lld, pair ok/drop %lld/%lld\n", | ||
| 330 | (long long)nRegisterOk, (long long)nRegisterFailed, | ||
| 331 | (long long)nPairOk, (long long)nPairFailed); | ||
| 332 | str += strprintf(" handshake grace silent %lld, no recv %lld, no send %lld\n", | ||
| 333 | (long long)nHandshakeSilent, (long long)nHandshakeNoRecv, | ||
| 334 | (long long)nHandshakeNoSend); | ||
| 335 | if (!strLastDialFail.empty()) | ||
| 336 | str += strprintf(" last dial failure %s\n", strLastDialFail.c_str()); | ||
| 337 | if (!strLastHandshakeTimeout.empty()) | ||
| 338 | str += strprintf(" last handshake %s\n", strLastHandshakeTimeout.c_str()); | ||
| 339 | |||
| 340 | str += "\n peer dir height last recv last send unsent via\n"; | ||
| 341 | foreach(CNode* pnode, vCopy) | ||
| 342 | { | ||
| 343 | int nSendSize = 0; | ||
| 344 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | ||
| 345 | nSendSize = (int)pnode->vSend.size(); | ||
| 346 | string strVia; | ||
| 347 | if (!pnode->strBtfMeeting.empty()) | ||
| 348 | strVia = pnode->strBtfMeeting; | ||
| 349 | str += strprintf(" %-28s %-4s %6d %10s %10s %7d %s\n", | ||
| 350 | pnode->addr.ToString().substr(0, 28).c_str(), | ||
| 351 | pnode->fInbound ? "in" : "out", | ||
| 352 | pnode->nStartingHeight, | ||
| 353 | FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(), | ||
| 354 | FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(), | ||
| 355 | nSendSize, | ||
| 356 | strVia.substr(0, 28).c_str()); | ||
| 357 | } | ||
| 358 | return str; | ||
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | bool GetMyExternalIP(unsigned int& ipRet) | ||
| 363 | { | ||
| 364 | // Try several plain-text IP echo services in order. | ||
| 365 | // Each returns just the IPv4 address as the first line of the HTTP body. | ||
| 366 | struct { const char* host; const char* path; } services[] = { | ||
| 367 | { "api4.ipify.org", "/" }, | ||
| 368 | { "icanhazip.com", "/" }, | ||
| 369 | { "ipecho.net", "/plain" }, | ||
| 370 | { "checkip.amazonaws.com", "/" }, | ||
| 371 | }; | ||
| 372 | |||
| 373 | for (auto& svc : services) | ||
| 374 | { | ||
| 375 | struct addrinfo hints, *res = NULL; | ||
| 376 | memset(&hints, 0, sizeof(hints)); | ||
| 377 | hints.ai_family = AF_INET; | ||
| 378 | hints.ai_socktype = SOCK_STREAM; | ||
| 379 | if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) | ||
| 380 | continue; | ||
| 381 | |||
| 382 | SOCKET hSocket = BtfSocketTag(socket(res->ai_family, res->ai_socktype, res->ai_protocol), SOCK_EXTIP); | ||
| 383 | if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } | ||
| 384 | |||
| 385 | // 5-second timeout so a dead service doesn't stall startup | ||
| 386 | #ifdef _WIN32 | ||
| 387 | DWORD tv = 5000; | ||
| 388 | setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); | ||
| 389 | setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); | ||
| 390 | #else | ||
| 391 | struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; | ||
| 392 | setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | ||
| 393 | setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); | ||
| 394 | #endif | ||
| 395 | |||
| 396 | if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) | ||
| 397 | { | ||
| 398 | freeaddrinfo(res); BtfCloseSocket(hSocket); continue; | ||
| 399 | } | ||
| 400 | freeaddrinfo(res); | ||
| 401 | |||
| 402 | string req = string("GET ") + svc.path + " HTTP/1.0\r\nHost: " + svc.host + "\r\nConnection: close\r\n\r\n"; | ||
| 403 | send(hSocket, req.c_str(), (int)req.size(), 0); | ||
| 404 | |||
| 405 | // Read response, skip HTTP headers, grab first line of body | ||
| 406 | string response; | ||
| 407 | char buf[256]; | ||
| 408 | int n; | ||
| 409 | while ((n = recv(hSocket, buf, sizeof(buf)-1, 0)) > 0) | ||
| 410 | { | ||
| 411 | buf[n] = 0; | ||
| 412 | response += buf; | ||
| 413 | if (response.size() > 4096) break; | ||
| 414 | } | ||
| 415 | BtfCloseSocket(hSocket); | ||
| 416 | |||
| 417 | // Find blank line separating headers from body | ||
| 418 | size_t bodyPos = response.find("\r\n\r\n"); | ||
| 419 | if (bodyPos == string::npos) bodyPos = response.find("\n\n"); | ||
| 420 | if (bodyPos == string::npos) continue; | ||
| 421 | string body = response.substr(bodyPos + (response[bodyPos+2]=='\r' ? 4 : 2)); | ||
| 422 | |||
| 423 | // Trim whitespace | ||
| 424 | while (!body.empty() && (body.back() == '\n' || body.back() == '\r' || body.back() == ' ')) | ||
| 425 | body.pop_back(); | ||
| 426 | |||
| 427 | // Parse as IP | ||
| 428 | CAddress addr(body.c_str()); | ||
| 429 | if (addr.ip == 0) continue; | ||
| 430 | |||
| 431 | printf("GetMyExternalIP() via %s: %s\n", svc.host, body.c_str()); | ||
| 432 | ipRet = addr.ip; | ||
| 433 | return true; | ||
| 434 | } | ||
| 435 | |||
| 436 | return error("GetMyExternalIP() : all services failed\n"); | ||
| 437 | } | ||
| 438 | |||
| 439 | |||
| 440 | |||
| 441 | |||
| 442 | |||
| 443 | |||
| 444 | |||
| 445 | |||
| 446 | |||
| 447 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | ||
| 448 | { | ||
| 449 | // If the dialog might get closed before the reply comes back, | ||
| 450 | // call this in the destructor so it doesn't get called after it's deleted. | ||
| 451 | CRITICAL_BLOCK(cs_vNodes) | ||
| 452 | { | ||
| 453 | foreach(CNode* pnode, vNodes) | ||
| 454 | { | ||
| 455 | CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 456 | { | ||
| 457 | for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) | ||
| 458 | { | ||
| 459 | CRequestTracker& tracker = (*mi).second; | ||
| 460 | if (tracker.fn == fn && tracker.param1 == param1) | ||
| 461 | pnode->mapRequests.erase(mi++); | ||
| 462 | else | ||
| 463 | mi++; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | |||
| 471 | |||
| 472 | |||
| 473 | |||
| 474 | |||
| 475 | |||
| 476 | // | ||
| 477 | // Subscription methods for the broadcast and subscription system. | ||
| 478 | // Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. | ||
| 479 | // | ||
| 480 | // The subscription system uses a meet-in-the-middle strategy. | ||
| 481 | // With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers | ||
| 482 | // subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. | ||
| 483 | // | ||
| 484 | |||
| 485 | bool AnySubscribed(unsigned int nChannel) | ||
| 486 | { | ||
| 487 | if (pnodeLocalHost->IsSubscribed(nChannel)) | ||
| 488 | return true; | ||
| 489 | CRITICAL_BLOCK(cs_vNodes) | ||
| 490 | foreach(CNode* pnode, vNodes) | ||
| 491 | if (pnode->IsSubscribed(nChannel)) | ||
| 492 | return true; | ||
| 493 | return false; | ||
| 494 | } | ||
| 495 | |||
| 496 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | ||
| 497 | { | ||
| 498 | // Filter out duplicate requests. | ||
| 499 | if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) | ||
| 500 | return; | ||
| 501 | pindexLastGetBlocksBegin = pindexBegin; | ||
| 502 | hashLastGetBlocksEnd = hashEnd; | ||
| 503 | |||
| 504 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); | ||
| 505 | } | ||
| 506 | |||
| 507 | bool CNode::IsSubscribed(unsigned int nChannel) | ||
| 508 | { | ||
| 509 | if (nChannel >= vfSubscribe.size()) | ||
| 510 | return false; | ||
| 511 | return vfSubscribe[nChannel]; | ||
| 512 | } | ||
| 513 | |||
| 514 | void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) | ||
| 515 | { | ||
| 516 | if (nChannel >= vfSubscribe.size()) | ||
| 517 | return; | ||
| 518 | |||
| 519 | if (!AnySubscribed(nChannel)) | ||
| 520 | { | ||
| 521 | // Relay subscribe | ||
| 522 | CRITICAL_BLOCK(cs_vNodes) | ||
| 523 | foreach(CNode* pnode, vNodes) | ||
| 524 | if (pnode != this) | ||
| 525 | pnode->PushMessage("subscribe", nChannel, nHops); | ||
| 526 | } | ||
| 527 | |||
| 528 | vfSubscribe[nChannel] = true; | ||
| 529 | } | ||
| 530 | |||
| 531 | void CNode::CancelSubscribe(unsigned int nChannel) | ||
| 532 | { | ||
| 533 | if (nChannel >= vfSubscribe.size()) | ||
| 534 | return; | ||
| 535 | |||
| 536 | // Prevent from relaying cancel if wasn't subscribed | ||
| 537 | if (!vfSubscribe[nChannel]) | ||
| 538 | return; | ||
| 539 | vfSubscribe[nChannel] = false; | ||
| 540 | |||
| 541 | if (!AnySubscribed(nChannel)) | ||
| 542 | { | ||
| 543 | // Relay subscription cancel | ||
| 544 | CRITICAL_BLOCK(cs_vNodes) | ||
| 545 | foreach(CNode* pnode, vNodes) | ||
| 546 | if (pnode != this) | ||
| 547 | pnode->PushMessage("sub-cancel", nChannel); | ||
| 548 | |||
| 549 | // Clear memory, no longer subscribed | ||
| 550 | if (nChannel == MSG_PRODUCT) | ||
| 551 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 552 | mapProducts.clear(); | ||
| 553 | } | ||
| 554 | } | ||
| 555 | |||
| 556 | |||
| 557 | |||
| 558 | |||
| 559 | |||
| 560 | |||
| 561 | |||
| 562 | |||
| 563 | |||
| 564 | CNode* FindNode(unsigned int ip) | ||
| 565 | { | ||
| 566 | CRITICAL_BLOCK(cs_vNodes) | ||
| 567 | { | ||
| 568 | foreach(CNode* pnode, vNodes) | ||
| 569 | if (pnode->addr.ip == ip) | ||
| 570 | return (pnode); | ||
| 571 | } | ||
| 572 | return NULL; | ||
| 573 | } | ||
| 574 | |||
| 575 | // Synthetic marker address for a tunneled `.btf` peer. The real endpoint is | ||
| 576 | // unknown by design (the tunnel hides it), so tag the CNode with a non-routable | ||
| 577 | // 10.x.x.x address (fails IsRoutable, so it is never gossiped as a real peer) | ||
| 578 | // that is stable per identity, keeping FindNode and the duplicate check working. | ||
| 579 | static CAddress BtfMarkerAddr(const unsigned char b5[5]) | ||
| 580 | { | ||
| 581 | unsigned int ip; | ||
| 582 | unsigned char b[4] = { 10, b5[0], b5[1], b5[2] }; | ||
| 583 | memcpy(&ip, b, 4); | ||
| 584 | unsigned short port = htons((unsigned short)((b5[3] << 8) | b5[4])); | ||
| 585 | return CAddress(ip, port, nLocalServices); | ||
| 586 | } | ||
| 587 | |||
| 588 | // Shared tail: given a peer's pubkey and already-resolved rendezvous | ||
| 589 | // coordinates, open the tunnel and register the CNode. Used by both | ||
| 590 | // ConnectNodeBtf (which resolves the descriptor itself) and | ||
| 591 | // ConnectNodeBtfResolved (which takes an already-resolved descriptor, so it | ||
| 592 | // makes no Nostr relay calls at all -- see nostr.cpp's BtfResolveMany for why | ||
| 593 | // that matters when dialing many candidates in parallel). | ||
| 594 | // | ||
| 595 | // Peers we have actually reached, remembered across restarts. | ||
| 596 | // | ||
| 597 | // Discovery is otherwise entirely dependent on the Nostr relays: a node that | ||
| 598 | // has been running for hours and knows exactly which peers answer throws all | ||
| 599 | // of it away on exit, and the next start walks the same ~96%-dead descriptor | ||
| 600 | // list again. Reconnecting from this file skips the relays completely -- | ||
| 601 | // address, meeting node and encryption key are all we need. | ||
| 602 | // | ||
| 603 | static const size_t MAX_CACHED_BTF_PEERS = 50; | ||
| 604 | static const int64 CACHED_BTF_PEER_TTL = 7 * 24 * 60 * 60; // a week | ||
| 605 | |||
| 606 | struct CachedBtfPeer | ||
| 607 | { | ||
| 608 | string btfAddr; | ||
| 609 | string meeting; | ||
| 610 | string encHex; | ||
| 611 | int64 lastSeen; | ||
| 612 | // The peer's own signed descriptor, as it announced itself. Kept verbatim | ||
| 613 | // because that signature is what lets us hand this peer on to somebody | ||
| 614 | // else: the receiver checks it against the key the address decodes to and | ||
| 615 | // never has to take our word for anything. Empty for entries learned | ||
| 616 | // before peer exchange existed, or resolved through Nostr -- those are | ||
| 617 | // still dialable, just not relayable. | ||
| 618 | string desc; | ||
| 619 | }; | ||
| 620 | |||
| 621 | static string BtfPeerCachePath() { return GetAppDir() + "/btfpeers.json"; } | ||
| 622 | |||
| 623 | static string BytesToHex(const unsigned char* p, size_t n) | ||
| 624 | { | ||
| 625 | static const char* h = "0123456789abcdef"; | ||
| 626 | string s; | ||
| 627 | s.reserve(n * 2); | ||
| 628 | for (size_t i = 0; i < n; i++) { s += h[p[i] >> 4]; s += h[p[i] & 0xf]; } | ||
| 629 | return s; | ||
| 630 | } | ||
| 631 | |||
| 632 | static bool HexToBytes(const string& s, unsigned char* out, size_t n) | ||
| 633 | { | ||
| 634 | if (s.size() != n * 2) return false; | ||
| 635 | for (size_t i = 0; i < n; i++) | ||
| 636 | { | ||
| 637 | unsigned int b; | ||
| 638 | if (sscanf(s.c_str() + i * 2, "%2x", &b) != 1) return false; | ||
| 639 | out[i] = (unsigned char)b; | ||
| 640 | } | ||
| 641 | return true; | ||
| 642 | } | ||
| 643 | |||
| 644 | void LoadCachedBtfPeers(vector<CachedBtfPeer>& out) | ||
| 645 | { | ||
| 646 | out.clear(); | ||
| 647 | FILE* f = fopen(BtfPeerCachePath().c_str(), "r"); | ||
| 648 | if (!f) return; | ||
| 649 | string body; | ||
| 650 | char buf[4096]; | ||
| 651 | size_t r; | ||
| 652 | while ((r = fread(buf, 1, sizeof(buf), f)) > 0) body.append(buf, r); | ||
| 653 | fclose(f); | ||
| 654 | try | ||
| 655 | { | ||
| 656 | nlohmann::json arr = nlohmann::json::parse(body); | ||
| 657 | if (!arr.is_array()) return; | ||
| 658 | int64 nNow = GetTime(); | ||
| 659 | for (const auto& o : arr) | ||
| 660 | { | ||
| 661 | if (!o.is_object()) continue; | ||
| 662 | if (!o.contains("btf") || !o["btf"].is_string()) continue; | ||
| 663 | if (!o.contains("meeting") || !o["meeting"].is_string()) continue; | ||
| 664 | if (!o.contains("enc") || !o["enc"].is_string()) continue; | ||
| 665 | CachedBtfPeer p; | ||
| 666 | p.btfAddr = o["btf"].get<string>(); | ||
| 667 | p.meeting = o["meeting"].get<string>(); | ||
| 668 | p.encHex = o["enc"].get<string>(); | ||
| 669 | p.lastSeen = o.value("seen", (int64)0); | ||
| 670 | if (o.contains("desc") && o["desc"].is_string()) | ||
| 671 | p.desc = o["desc"].get<string>(); | ||
| 672 | if (nNow - p.lastSeen > CACHED_BTF_PEER_TTL) continue; // long gone | ||
| 673 | out.push_back(p); | ||
| 674 | } | ||
| 675 | } | ||
| 676 | catch (...) | ||
| 677 | { | ||
| 678 | LogPrint("net", "btfpeers: cache unreadable, starting empty\n"); | ||
| 679 | out.clear(); | ||
| 680 | } | ||
| 681 | } | ||
| 682 | |||
| 683 | static CCriticalSection cs_btfPeerCache; | ||
| 684 | |||
| 685 | // strDesc is the peer's own signed descriptor when we have it (it announced | ||
| 686 | // itself over peer exchange), "" when we only resolved it through Nostr. An | ||
| 687 | // empty one never clears a descriptor already on file: dialing a peer we first | ||
| 688 | // learned about by exchange must not cost us the ability to pass it on. | ||
| 689 | static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | ||
| 690 | const unsigned char enc_pub[32], | ||
| 691 | const string& strDesc = string()) | ||
| 692 | { | ||
| 693 | CRITICAL_BLOCK(cs_btfPeerCache) | ||
| 694 | { | ||
| 695 | vector<CachedBtfPeer> peers; | ||
| 696 | LoadCachedBtfPeers(peers); | ||
| 697 | |||
| 698 | string strKeepDesc = strDesc; | ||
| 699 | for (size_t i = 0; i < peers.size(); i++) | ||
| 700 | if (peers[i].btfAddr == strBtfAddr) | ||
| 701 | { | ||
| 702 | if (strKeepDesc.empty()) | ||
| 703 | strKeepDesc = peers[i].desc; | ||
| 704 | peers.erase(peers.begin() + i); | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | |||
| 708 | CachedBtfPeer p; | ||
| 709 | p.btfAddr = strBtfAddr; | ||
| 710 | p.meeting = strMeeting; | ||
| 711 | p.encHex = BytesToHex(enc_pub, 32); | ||
| 712 | p.lastSeen = GetTime(); | ||
| 713 | p.desc = strKeepDesc; | ||
| 714 | peers.insert(peers.begin(), p); // most recent first | ||
| 715 | |||
| 716 | if (peers.size() > MAX_CACHED_BTF_PEERS) | ||
| 717 | peers.resize(MAX_CACHED_BTF_PEERS); | ||
| 718 | |||
| 719 | nlohmann::json arr = nlohmann::json::array(); | ||
| 720 | foreach(const CachedBtfPeer& q, peers) | ||
| 721 | { | ||
| 722 | nlohmann::json o; | ||
| 723 | o["btf"] = q.btfAddr; | ||
| 724 | o["meeting"] = q.meeting; | ||
| 725 | o["enc"] = q.encHex; | ||
| 726 | o["seen"] = q.lastSeen; | ||
| 727 | if (!q.desc.empty()) | ||
| 728 | o["desc"] = q.desc; | ||
| 729 | arr.push_back(o); | ||
| 730 | } | ||
| 731 | FILE* f = fopen(BtfPeerCachePath().c_str(), "w"); | ||
| 732 | if (f) | ||
| 733 | { | ||
| 734 | string s = arr.dump(2); | ||
| 735 | fwrite(s.c_str(), 1, s.size(), f); | ||
| 736 | fclose(f); | ||
| 737 | } | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | // | ||
| 742 | // Compiled-in bootstrap seeds. | ||
| 743 | // | ||
| 744 | // A node with no cache has nothing but the Nostr relays, and if those are down, | ||
| 745 | // blocked or simply slow, a first run has no way into the network at all. Seeds | ||
| 746 | // are the floor under that: a handful of long-lived peers baked into the binary. | ||
| 747 | // | ||
| 748 | // A seed entry is deliberately just an address and an encryption key -- no | ||
| 749 | // meeting node. The rendezvous relay pairs on the service's public key, and a | ||
| 750 | // `.btf` address *is* that key, so a client can find a seed by trying the known | ||
| 751 | // relays in turn. Recording which relay a seed was on would rot the moment it | ||
| 752 | // failed over, which is the exact failure this project spent a long time | ||
| 753 | // chasing: advertising a rendezvous somebody is not registered at. | ||
| 754 | // | ||
| 755 | // Seeds are ordinary nodes with no special authority. They hand out signed, | ||
| 756 | // self-certifying descriptors like any peer, so a hostile seed can stall a | ||
| 757 | // bootstrap but cannot forge a peer or feed a false chain. | ||
| 758 | // | ||
| 759 | struct BtfSeed | ||
| 760 | { | ||
| 761 | const char* btfAddr; | ||
| 762 | const char* encHex; // 64 hex chars, the peer's x25519 public key | ||
| 763 | }; | ||
| 764 | |||
| 765 | static const BtfSeed pszBtfSeeds[] = | ||
| 766 | { | ||
| 767 | // Dedicated bootstrap node, Almaty. Runs beside a rendezvous relay on the | ||
| 768 | // same host, holds no wallet balance and does not mine -- it exists only to | ||
| 769 | // answer a first dial. It is trusted for nothing: it serves the same signed | ||
| 770 | // descriptors any peer does. | ||
| 771 | { "ygffz37jczlmrkzicxok6chobauyratdexc7hfgwzjdabzvb2nkngqy.btf", | ||
| 772 | "de8284b9d4effa2132e7981b566c1a297c39c1857d9c128e8ae093ef511d3200" }, | ||
| 773 | }; | ||
| 774 | static const size_t nBtfSeeds = ARRAYLEN(pszBtfSeeds); | ||
| 775 | |||
| 776 | // Extra seeds from the command line (/btfseed=ADDRESS:ENCHEX, repeatable). | ||
| 777 | vector<pair<string, string> > vBtfExtraSeeds; | ||
| 778 | |||
| 779 | static int TryBtfSeeds() | ||
| 780 | { | ||
| 781 | vector<pair<string, string> > seeds; | ||
| 782 | for (size_t i = 0; i < nBtfSeeds; i++) | ||
| 783 | seeds.push_back(make_pair(string(pszBtfSeeds[i].btfAddr), | ||
| 784 | string(pszBtfSeeds[i].encHex))); | ||
| 785 | foreach(const PAIRTYPE(string, string)& s, vBtfExtraSeeds) | ||
| 786 | seeds.push_back(s); | ||
| 787 | |||
| 788 | if (seeds.empty()) | ||
| 789 | return 0; | ||
| 790 | |||
| 791 | vector<string> relays = BtfAllRelays(); | ||
| 792 | if (relays.empty()) | ||
| 793 | return 0; | ||
| 794 | |||
| 795 | LogPrint("net", "btfseed: trying %zu seed(s) across %zu relay(s)\n", | ||
| 796 | seeds.size(), relays.size()); | ||
| 797 | |||
| 798 | int nConnected = 0; | ||
| 799 | foreach(const PAIRTYPE(string, string)& s, seeds) | ||
| 800 | { | ||
| 801 | if (fShutdown) return nConnected; | ||
| 802 | unsigned char enc[32]; | ||
| 803 | if (!HexToBytes(s.second, enc, 32)) | ||
| 804 | { | ||
| 805 | LogPrint("net", "btfseed: %s has a malformed encryption key, skipped\n", | ||
| 806 | s.first.c_str()); | ||
| 807 | continue; | ||
| 808 | } | ||
| 809 | // We do not know which relay this seed is registered at, so walk them. | ||
| 810 | foreach(const string& strRelay, relays) | ||
| 811 | { | ||
| 812 | if (fShutdown) return nConnected; | ||
| 813 | if (ConnectNodeBtfResolved(s.first, strRelay, enc)) | ||
| 814 | { | ||
| 815 | LogPrint("net", "btfseed: reached %s via %s\n", | ||
| 816 | s.first.c_str(), strRelay.c_str()); | ||
| 817 | nConnected++; | ||
| 818 | break; // found it; no need to try this seed's other relays | ||
| 819 | } | ||
| 820 | } | ||
| 821 | if (nConnected >= 4) | ||
| 822 | break; // enough of a foothold; the rest comes from discovery | ||
| 823 | } | ||
| 824 | LogPrint("net", "btfseed: %d seed(s) answered\n", nConnected); | ||
| 825 | return nConnected; | ||
| 826 | } | ||
| 827 | |||
| 828 | // Dial everything we reached last time, before the relays have said anything. | ||
| 829 | void ThreadReconnectCachedBtfPeers(void* parg) | ||
| 830 | { | ||
| 831 | vector<CachedBtfPeer> peers; | ||
| 832 | LoadCachedBtfPeers(peers); | ||
| 833 | if (peers.empty()) | ||
| 834 | { | ||
| 835 | // First run, or the cache aged out. Seeds are the only way in that does | ||
| 836 | // not depend on a relay answering. | ||
| 837 | LogPrint("net", "btfpeers: no cache yet, falling back to seeds\n"); | ||
| 838 | if (TryBtfSeeds() == 0) | ||
| 839 | LogPrint("net", "btfpeers: no seed answered, waiting on relay discovery\n"); | ||
| 840 | return; | ||
| 841 | } | ||
| 842 | LogPrint("net", "btfpeers: trying %zu remembered peer(s) before discovery\n", | ||
| 843 | peers.size()); | ||
| 844 | |||
| 845 | int nConnected = 0; | ||
| 846 | foreach(const CachedBtfPeer& p, peers) | ||
| 847 | { | ||
| 848 | if (fShutdown) return; | ||
| 849 | unsigned char enc[32]; | ||
| 850 | if (!HexToBytes(p.encHex, enc, 32)) continue; | ||
| 851 | if (ConnectNodeBtfResolved(p.btfAddr, p.meeting, enc)) | ||
| 852 | { | ||
| 853 | nConnected++; | ||
| 854 | if (nConnected >= 8) break; // enough to bootstrap; the rest can wait | ||
| 855 | } | ||
| 856 | } | ||
| 857 | LogPrint("net", "btfpeers: %d of %zu remembered peer(s) answered\n", | ||
| 858 | nConnected, peers.size()); | ||
| 859 | } | ||
| 860 | |||
| 861 | // | ||
| 862 | // .btf peer exchange | ||
| 863 | // | ||
| 864 | // Until now discovery depended entirely on the Nostr relays: two connected | ||
| 865 | // nodes never told each other who else existed, so a relay outage left a | ||
| 866 | // running network unable to grow, and a node that found one stale peer had no | ||
| 867 | // way to learn there was anything better. | ||
| 868 | // | ||
| 869 | // What travels here is each peer's own signed descriptor. The receiver checks | ||
| 870 | // the Schnorr signature against the key the `.btf` address decodes to, so the | ||
| 871 | // sender is trusted for nothing -- forging an entry would need somebody else's | ||
| 872 | // secret key. That leaves flooding: valid descriptors for keys the sender | ||
| 873 | // generated itself. The caps below bound one message, and the cache prefers | ||
| 874 | // peers that actually answered, so a flood costs a Sybil more than it costs us. | ||
| 875 | // | ||
| 876 | |||
| 877 | void BtfPexCollect(vector<string>& vDescOut) | ||
| 878 | { | ||
| 879 | vDescOut.clear(); | ||
| 880 | |||
| 881 | // Ours first -- the node on the other end may know nobody but us. | ||
| 882 | string strMine = BtfLocalDescriptor(); | ||
| 883 | if (!strMine.empty()) | ||
| 884 | vDescOut.push_back(strMine); | ||
| 885 | |||
| 886 | vector<CachedBtfPeer> peers; | ||
| 887 | CRITICAL_BLOCK(cs_btfPeerCache) | ||
| 888 | LoadCachedBtfPeers(peers); | ||
| 889 | |||
| 890 | // Most recently seen first: these answered us, they are not names copied | ||
| 891 | // off a relay listing. | ||
| 892 | foreach(const CachedBtfPeer& p, peers) | ||
| 893 | { | ||
| 894 | if (vDescOut.size() >= MAX_PEX_DESCRIPTORS) break; | ||
| 895 | if (p.desc.empty()) continue; // nothing provable to pass on | ||
| 896 | if (p.desc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue; | ||
| 897 | vDescOut.push_back(p.desc); | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | int BtfPexAccept(const vector<string>& vDesc) | ||
| 902 | { | ||
| 903 | void* ctx = BtfSecpContext(); | ||
| 904 | if (!ctx) | ||
| 905 | return 0; | ||
| 906 | |||
| 907 | string strSelf = BtfLocalAddress(); | ||
| 908 | int nKept = 0; | ||
| 909 | unsigned int nSeen = 0; | ||
| 910 | |||
| 911 | foreach(const string& strDesc, vDesc) | ||
| 912 | { | ||
| 913 | if (++nSeen > MAX_PEX_DESCRIPTORS) break; | ||
| 914 | if (strDesc.empty() || strDesc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue; | ||
| 915 | |||
| 916 | // The descriptor names the key it belongs to, and taking that key from | ||
| 917 | // the blob is safe precisely because the signature must verify under | ||
| 918 | // it: nobody can produce a valid descriptor for a key they don't hold. | ||
| 919 | unsigned char pubkey[32]; | ||
| 920 | try | ||
| 921 | { | ||
| 922 | nlohmann::json v = nlohmann::json::parse(strDesc); | ||
| 923 | if (!v.is_object() || !v.contains("pubkey") || !v["pubkey"].is_string()) | ||
| 924 | continue; | ||
| 925 | if (!HexToBytes(v["pubkey"].get<string>(), pubkey, 32)) | ||
| 926 | continue; | ||
| 927 | } | ||
| 928 | catch (...) { continue; } // malformed JSON from an untrusted peer | ||
| 929 | |||
| 930 | btf::Descriptor d; | ||
| 931 | if (!btf::VerifyDescriptor(ctx, strDesc, pubkey, d)) | ||
| 932 | continue; | ||
| 933 | |||
| 934 | string strAddr = btf::Address(pubkey); | ||
| 935 | if (strAddr.empty() || strAddr == strSelf) | ||
| 936 | continue; // ourselves, nothing to learn | ||
| 937 | |||
| 938 | unsigned char enc[32]; | ||
| 939 | if (!HexToBytes(d.enc, enc, 32)) | ||
| 940 | continue; | ||
| 941 | |||
| 942 | RememberBtfPeer(strAddr, d.meeting_node, enc, strDesc); | ||
| 943 | nKept++; | ||
| 944 | } | ||
| 945 | return nKept; | ||
| 946 | } | ||
| 947 | |||
| 948 | static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], | ||
| 949 | const string& strMeeting, const unsigned char enc_pub[32]) | ||
| 950 | { | ||
| 951 | CAddress addr = BtfMarkerAddr(pk); | ||
| 952 | CNode* pnode = FindNode(addr.ip); | ||
| 953 | if (pnode) | ||
| 954 | { | ||
| 955 | pnode->AddRef(); | ||
| 956 | return pnode; | ||
| 957 | } | ||
| 958 | |||
| 959 | BtfChurnNoteDialAttempt(strBtfAddr, strMeeting); | ||
| 960 | size_t colon = strMeeting.rfind(':'); | ||
| 961 | if (colon == string::npos) | ||
| 962 | { | ||
| 963 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 964 | return NULL; | ||
| 965 | } | ||
| 966 | string strHost = strMeeting.substr(0, colon); | ||
| 967 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); | ||
| 968 | if (nPort <= 0 || nPort > 65535) | ||
| 969 | { | ||
| 970 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 971 | return NULL; | ||
| 972 | } | ||
| 973 | |||
| 974 | btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); | ||
| 975 | if (hSocket == INVALID_SOCKET) | ||
| 976 | { | ||
| 977 | if (fDebug) | ||
| 978 | LogPrint("net", "ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 979 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 980 | return NULL; | ||
| 981 | } | ||
| 982 | |||
| 983 | if (fDebug) | ||
| 984 | LogPrint("net", "connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 985 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, true); | ||
| 986 | |||
| 987 | // This one answered -- worth trying first next time we start. | ||
| 988 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub); | ||
| 989 | |||
| 990 | // Add node | ||
| 991 | pnode = new CNode(hSocket, addr, false); | ||
| 992 | pnode->strBtfAddr = strBtfAddr; | ||
| 993 | pnode->strBtfMeeting = strMeeting; | ||
| 994 | pnode->AddRef(); | ||
| 995 | CRITICAL_BLOCK(cs_vNodes) | ||
| 996 | vNodes.push_back(pnode); | ||
| 997 | return pnode; | ||
| 998 | } | ||
| 999 | |||
| 1000 | // Connect to a peer by its `.btf` address: resolve the self-certified | ||
| 1001 | // descriptor over Nostr, then tunnel through its meeting node with the | ||
| 1002 | // end-to-end channel. Neither side ever learns the other's IP. | ||
| 1003 | CNode* ConnectNodeBtf(const string& strBtfAddr) | ||
| 1004 | { | ||
| 1005 | unsigned char pk[32]; | ||
| 1006 | if (!btf::ParseAddress(strBtfAddr, pk)) | ||
| 1007 | { | ||
| 1008 | LogPrint("net", "ConnectNodeBtf: invalid address %s\n", strBtfAddr.c_str()); | ||
| 1009 | return NULL; | ||
| 1010 | } | ||
| 1011 | if (BtfLocalAddress() == strBtfAddr) | ||
| 1012 | return NULL; // ourselves | ||
| 1013 | |||
| 1014 | if (fDebug) | ||
| 1015 | LogPrint("net", "trying %s\n", strBtfAddr.c_str()); | ||
| 1016 | |||
| 1017 | string strMeeting; | ||
| 1018 | unsigned char enc_pub[32]; | ||
| 1019 | BtfChurnNoteResolveAttempt(); | ||
| 1020 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) | ||
| 1021 | { | ||
| 1022 | BtfChurnNoteResolveResult(false); | ||
| 1023 | if (fDebug) | ||
| 1024 | LogPrint("net", "ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); | ||
| 1025 | return NULL; | ||
| 1026 | } | ||
| 1027 | BtfChurnNoteResolveResult(true); | ||
| 1028 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | // Same as ConnectNodeBtf, but skips the Nostr resolve step because the caller | ||
| 1032 | // already resolved the descriptor (e.g. via BtfResolveMany, batching many | ||
| 1033 | // addresses into one relay round-trip instead of one relay round-trip per | ||
| 1034 | // address). Makes zero relay connections -- only talks to the peer's | ||
| 1035 | // rendezvous meeting node. | ||
| 1036 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, | ||
| 1037 | const unsigned char enc_pub[32]) | ||
| 1038 | { | ||
| 1039 | unsigned char pk[32]; | ||
| 1040 | if (!btf::ParseAddress(strBtfAddr, pk)) | ||
| 1041 | { | ||
| 1042 | LogPrint("net", "ConnectNodeBtf: invalid address %s\n", strBtfAddr.c_str()); | ||
| 1043 | return NULL; | ||
| 1044 | } | ||
| 1045 | if (BtfLocalAddress() == strBtfAddr) | ||
| 1046 | return NULL; // ourselves | ||
| 1047 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | // Anonymous inbound listener (this node's `.btf` hidden service). Registers at | ||
| 1051 | // the meeting relay and blocks until a client dials our address; each pairing | ||
| 1052 | // becomes a normal inbound CNode riding the end-to-end channel, then we | ||
| 1053 | // register again for the next caller. | ||
| 1054 | void ThreadBtfAccept(void* parg) | ||
| 1055 | { | ||
| 1056 | printf("ThreadBtfAccept started\n"); | ||
| 1057 | unsigned char pk[32]; | ||
| 1058 | unsigned char sk[32]; | ||
| 1059 | while (!BtfGetIdentity(pk, sk)) | ||
| 1060 | { | ||
| 1061 | if (fShutdown) | ||
| 1062 | return; | ||
| 1063 | Sleep(5000); | ||
| 1064 | } | ||
| 1065 | size_t iRelay = 0; | ||
| 1066 | bool fPicked = false; | ||
| 1067 | loop | ||
| 1068 | { | ||
| 1069 | if (fShutdown) | ||
| 1070 | return; | ||
| 1071 | // Curated seeds + relays discovered from Nostr announcements. | ||
| 1072 | vector<string> relays = BtfAllRelays(); | ||
| 1073 | if (relays.empty()) | ||
| 1074 | { | ||
| 1075 | Sleep(10000); // no meeting relay configured | ||
| 1076 | continue; | ||
| 1077 | } | ||
| 1078 | // Random start spreads nodes across all relays (incl. volunteer ones). | ||
| 1079 | if (!fPicked) | ||
| 1080 | { | ||
| 1081 | iRelay = (size_t)GetRand(relays.size()); | ||
| 1082 | fPicked = true; | ||
| 1083 | } | ||
| 1084 | // Stick to the current relay while it works; on failure, fail over to | ||
| 1085 | // the next one so a DDoS'd/blocked relay IP can't keep us offline. | ||
| 1086 | string strMeeting = relays[iRelay % relays.size()]; | ||
| 1087 | size_t colon = strMeeting.rfind(':'); | ||
| 1088 | if (colon == string::npos) | ||
| 1089 | { | ||
| 1090 | iRelay++; | ||
| 1091 | Sleep(2000); | ||
| 1092 | continue; | ||
| 1093 | } | ||
| 1094 | string strHost = strMeeting.substr(0, colon); | ||
| 1095 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); | ||
| 1096 | |||
| 1097 | // Returns as soon as the relay has us listed -- it does not wait for a | ||
| 1098 | // dial. That distinction is the whole point: we have to be advertised | ||
| 1099 | // before anyone can dial us, so registering and waiting cannot be the | ||
| 1100 | // same call. | ||
| 1101 | btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk); | ||
| 1102 | if (fShutdown) | ||
| 1103 | { | ||
| 1104 | if (rv != btf::RV_INVALID) | ||
| 1105 | btf::RvClose(rv); | ||
| 1106 | return; | ||
| 1107 | } | ||
| 1108 | if (rv == btf::RV_INVALID) | ||
| 1109 | { | ||
| 1110 | BtfChurnNoteRegisterResult(strMeeting, false); | ||
| 1111 | LogPrint("net", "rendezvous: could not register at %s, trying another\n", | ||
| 1112 | strMeeting.c_str()); | ||
| 1113 | iRelay++; // this relay is down/attacked -> try the next one | ||
| 1114 | Sleep(3000); | ||
| 1115 | continue; | ||
| 1116 | } | ||
| 1117 | BtfChurnNoteRegisterResult(strMeeting, true); | ||
| 1118 | |||
| 1119 | // Registered. Advertise THIS relay now, while we are listed and before | ||
| 1120 | // anybody dials -- a descriptor naming it is what makes a dial possible | ||
| 1121 | // at all. Keep using it (iRelay unchanged) until it fails. | ||
| 1122 | BtfSetActiveRelay(strMeeting); | ||
| 1123 | LogPrint("net", "rendezvous: registered at %s, waiting for a dial\n", | ||
| 1124 | strMeeting.c_str()); | ||
| 1125 | |||
| 1126 | // Now wait for someone to arrive -- but not forever. This used to have | ||
| 1127 | // no bound, and a registration whose path died quietly left the thread | ||
| 1128 | // parked in recv() with nothing to wake it: the loop never came back | ||
| 1129 | // here, the node stopped being reachable, and nothing in the log said | ||
| 1130 | // so. Re-registering every few minutes when nobody has dialled costs | ||
| 1131 | // one reconnect and removes the whole failure mode. | ||
| 1132 | bool fPaired = btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS); | ||
| 1133 | BtfChurnNotePairResult(strMeeting, fPaired); | ||
| 1134 | if (!fPaired) | ||
| 1135 | { | ||
| 1136 | btf::RvClose(rv); | ||
| 1137 | LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), " | ||
| 1138 | "re-registering\n", strMeeting.c_str(), BTF_RENDEZVOUS_WAIT_SECS); | ||
| 1139 | continue; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); | ||
| 1143 | if (hSocket == INVALID_SOCKET) | ||
| 1144 | { | ||
| 1145 | LogPrint("net", "rendezvous: paired at %s but channel setup failed\n", | ||
| 1146 | strMeeting.c_str()); | ||
| 1147 | continue; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | // The dialer is anonymous (its pubkey never reaches us), so tag the | ||
| 1151 | // connection with a random marker address. | ||
| 1152 | unsigned char rnd[5]; | ||
| 1153 | RAND_bytes(rnd, sizeof(rnd)); | ||
| 1154 | CAddress addr = BtfMarkerAddr(rnd); | ||
| 1155 | |||
| 1156 | LogPrint("net", "accepted .btf connection via rendezvous %s\n", strMeeting.c_str()); | ||
| 1157 | CNode* pnode = new CNode(hSocket, addr, true); | ||
| 1158 | pnode->strBtfAddr = "inbound"; | ||
| 1159 | pnode->strBtfMeeting = strMeeting; | ||
| 1160 | pnode->AddRef(); | ||
| 1161 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1162 | vNodes.push_back(pnode); | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | // Keep an outbound connection to a specific `.btf` peer (from /connectbtf) -- | ||
| 1167 | // the rendezvous analogue of addnode. Retries until connected, reconnects if | ||
| 1168 | // the tunnel drops. | ||
| 1169 | void ThreadBtfConnect(void* parg) | ||
| 1170 | { | ||
| 1171 | printf("ThreadBtfConnect started\n"); | ||
| 1172 | string strAddr = *(string*)parg; | ||
| 1173 | delete (string*)parg; | ||
| 1174 | loop | ||
| 1175 | { | ||
| 1176 | if (fShutdown) | ||
| 1177 | return; | ||
| 1178 | try | ||
| 1179 | { | ||
| 1180 | CNode* pnode = ConnectNodeBtf(strAddr); | ||
| 1181 | if (pnode) | ||
| 1182 | { | ||
| 1183 | if (!pnode->fNetworkNode) | ||
| 1184 | pnode->fNetworkNode = true; // keep the ref from ConnectNodeBtf | ||
| 1185 | else | ||
| 1186 | pnode->Release(); // already pinned; drop the extra ref | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | CATCH_PRINT_EXCEPTION("ThreadBtfConnect") | ||
| 1190 | for (int i = 0; i < 30 && !fShutdown; i++) | ||
| 1191 | Sleep(1000); | ||
| 1192 | } | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | void CNode::Disconnect() | ||
| 1196 | { | ||
| 1197 | LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); | ||
| 1198 | |||
| 1199 | // Invalidate before anything else can look at it. This used to close the | ||
| 1200 | // socket and leave the handle number sitting in the object, so ~CNode | ||
| 1201 | // closed it a second time and the select loop kept arming it in the | ||
| 1202 | // meantime. See the note on ~CNode in net.h. | ||
| 1203 | BtfCloseSocket(hSocket); | ||
| 1204 | hSocket = INVALID_SOCKET; | ||
| 1205 | |||
| 1206 | // All of a nodes broadcasts and subscriptions are automatically torn down | ||
| 1207 | // when it goes down, so a node has to stay up to keep its broadcast going. | ||
| 1208 | |||
| 1209 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 1210 | for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();) | ||
| 1211 | AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second); | ||
| 1212 | |||
| 1213 | // Cancel subscriptions | ||
| 1214 | for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) | ||
| 1215 | if (vfSubscribe[nChannel]) | ||
| 1216 | CancelSubscribe(nChannel); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | |||
| 1220 | |||
| 1221 | |||
| 1222 | |||
| 1223 | |||
| 1224 | |||
| 1225 | |||
| 1226 | |||
| 1227 | |||
| 1228 | |||
| 1229 | |||
| 1230 | |||
| 1231 | void ThreadSocketHandler(void* parg) | ||
| 1232 | { | ||
| 1233 | IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); | ||
| 1234 | |||
| 1235 | loop | ||
| 1236 | { | ||
| 1237 | vfThreadRunning[0] = true; | ||
| 1238 | CheckForShutdown(0); | ||
| 1239 | try | ||
| 1240 | { | ||
| 1241 | ThreadSocketHandler2(parg); | ||
| 1242 | } | ||
| 1243 | CATCH_PRINT_EXCEPTION("ThreadSocketHandler()") | ||
| 1244 | vfThreadRunning[0] = false; | ||
| 1245 | Sleep(5000); | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | void ThreadSocketHandler2(void* parg) | ||
| 1250 | { | ||
| 1251 | printf("ThreadSocketHandler started\n"); | ||
| 1252 | SOCKET hListenSocket = *(SOCKET*)parg; | ||
| 1253 | list<CNode*> vNodesDisconnected; | ||
| 1254 | int nPrevNodeCount = 0; | ||
| 1255 | |||
| 1256 | loop | ||
| 1257 | { | ||
| 1258 | // | ||
| 1259 | // Disconnect nodes | ||
| 1260 | // | ||
| 1261 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1262 | { | ||
| 1263 | // Disconnect duplicate connections | ||
| 1264 | map<unsigned int, CNode*> mapFirst; | ||
| 1265 | foreach(CNode* pnode, vNodes) | ||
| 1266 | { | ||
| 1267 | if (pnode->fDisconnect) | ||
| 1268 | continue; | ||
| 1269 | unsigned int ip = pnode->addr.ip; | ||
| 1270 | if (mapFirst.count(ip) && addrLocalHost.ip < ip) | ||
| 1271 | { | ||
| 1272 | // In case two nodes connect to each other at once, | ||
| 1273 | // the lower ip disconnects its outbound connection | ||
| 1274 | CNode* pnodeExtra = mapFirst[ip]; | ||
| 1275 | |||
| 1276 | if (pnodeExtra->GetRefCount() > (pnodeExtra->fNetworkNode ? 1 : 0)) | ||
| 1277 | swap(pnodeExtra, pnode); | ||
| 1278 | |||
| 1279 | if (pnodeExtra->GetRefCount() <= (pnodeExtra->fNetworkNode ? 1 : 0)) | ||
| 1280 | { | ||
| 1281 | LogPrint("net", "(%d nodes) disconnecting duplicate: %s\n", (int)vNodes.size(), pnodeExtra->addr.ToString().c_str()); | ||
| 1282 | if (pnodeExtra->fNetworkNode && !pnode->fNetworkNode) | ||
| 1283 | { | ||
| 1284 | pnode->AddRef(); | ||
| 1285 | swap(pnodeExtra->fNetworkNode, pnode->fNetworkNode); | ||
| 1286 | pnodeExtra->Release(); | ||
| 1287 | } | ||
| 1288 | pnodeExtra->fDisconnect = true; | ||
| 1289 | } | ||
| 1290 | } | ||
| 1291 | mapFirst[ip] = pnode; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | // Disconnect unused nodes | ||
| 1295 | vector<CNode*> vNodesCopy = vNodes; | ||
| 1296 | foreach(CNode* pnode, vNodesCopy) | ||
| 1297 | { | ||
| 1298 | // Wait for the buffers to drain before letting a node go, so a | ||
| 1299 | // last message still gets out -- but not forever. vSend never | ||
| 1300 | // drains through a socket whose far end is gone, and a node | ||
| 1301 | // stuck that way stayed in vNodes for the life of the process, | ||
| 1302 | // holding its socket and counting as a peer. | ||
| 1303 | if (!pnode->ReadyToDisconnect()) | ||
| 1304 | pnode->nDisconnectSince = 0; | ||
| 1305 | else if (pnode->nDisconnectSince == 0) | ||
| 1306 | pnode->nDisconnectSince = GetTime(); | ||
| 1307 | |||
| 1308 | bool fDrained = pnode->vRecv.empty() && pnode->vSend.empty(); | ||
| 1309 | bool fStuck = pnode->nDisconnectSince != 0 && | ||
| 1310 | GetTime() - pnode->nDisconnectSince > DISCONNECT_DRAIN_SECS; | ||
| 1311 | |||
| 1312 | if (pnode->ReadyToDisconnect() && (fDrained || fStuck)) | ||
| 1313 | { | ||
| 1314 | if (!fDrained) | ||
| 1315 | printf("dropping stuck node %s after %d s with %d bytes unsent\n", | ||
| 1316 | pnode->addr.ToString().c_str(), | ||
| 1317 | (int)(GetTime() - pnode->nDisconnectSince), | ||
| 1318 | (int)pnode->vSend.size()); | ||
| 1319 | // remove from vNodes | ||
| 1320 | vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); | ||
| 1321 | pnode->Disconnect(); | ||
| 1322 | |||
| 1323 | // hold in disconnected pool until all refs are released | ||
| 1324 | pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60); | ||
| 1325 | if (pnode->fNetworkNode) | ||
| 1326 | pnode->Release(); | ||
| 1327 | vNodesDisconnected.push_back(pnode); | ||
| 1328 | } | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | // Delete disconnected nodes | ||
| 1332 | list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; | ||
| 1333 | foreach(CNode* pnode, vNodesDisconnectedCopy) | ||
| 1334 | { | ||
| 1335 | // wait until threads are done using it | ||
| 1336 | if (pnode->GetRefCount() <= 0) | ||
| 1337 | { | ||
| 1338 | bool fDelete = false; | ||
| 1339 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | ||
| 1340 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | ||
| 1341 | TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 1342 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) | ||
| 1343 | fDelete = true; | ||
| 1344 | if (fDelete) | ||
| 1345 | { | ||
| 1346 | vNodesDisconnected.remove(pnode); | ||
| 1347 | delete pnode; | ||
| 1348 | } | ||
| 1349 | } | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | if (vNodes.size() != nPrevNodeCount) | ||
| 1353 | { | ||
| 1354 | nPrevNodeCount = vNodes.size(); | ||
| 1355 | MainFrameRepaint(); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | |||
| 1359 | // | ||
| 1360 | // Find which sockets have data to receive | ||
| 1361 | // | ||
| 1362 | struct timeval timeout; | ||
| 1363 | timeout.tv_sec = 0; | ||
| 1364 | timeout.tv_usec = 50000; // frequency to poll pnode->vSend | ||
| 1365 | |||
| 1366 | fd_set fdsetRecv; | ||
| 1367 | fd_set fdsetSend; | ||
| 1368 | FD_ZERO(&fdsetRecv); | ||
| 1369 | FD_ZERO(&fdsetSend); | ||
| 1370 | SOCKET hSocketMax = 0; | ||
| 1371 | FD_SET(hListenSocket, &fdsetRecv); | ||
| 1372 | hSocketMax = max(hSocketMax, hListenSocket); | ||
| 1373 | // FD_SET drops silently once the set is full, so count what goes in | ||
| 1374 | // and say so. A peer that never makes it into the set is never read, | ||
| 1375 | // and that is indistinguishable from a peer with nothing to say -- | ||
| 1376 | // the failure this whole path used to hide. | ||
| 1377 | unsigned int nWatched = 1; // the listen socket | ||
| 1378 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1379 | { | ||
| 1380 | foreach(CNode* pnode, vNodes) | ||
| 1381 | { | ||
| 1382 | if (nWatched >= FD_SETSIZE) | ||
| 1383 | break; | ||
| 1384 | // A disconnected node stays in vNodes until its references go. | ||
| 1385 | // Arming its closed handle asks select() to watch a number the | ||
| 1386 | // operating system may already have given to somebody else. | ||
| 1387 | if (pnode->hSocket == INVALID_SOCKET) | ||
| 1388 | continue; | ||
| 1389 | FD_SET(pnode->hSocket, &fdsetRecv); | ||
| 1390 | nWatched++; | ||
| 1391 | hSocketMax = max(hSocketMax, pnode->hSocket); | ||
| 1392 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | ||
| 1393 | if (!pnode->vSend.empty()) | ||
| 1394 | FD_SET(pnode->hSocket, &fdsetSend); | ||
| 1395 | } | ||
| 1396 | if (vNodes.size() + 1 > (size_t)FD_SETSIZE) | ||
| 1397 | { | ||
| 1398 | static int64 nLastWarned = 0; | ||
| 1399 | if (GetTime() - nLastWarned > 60) | ||
| 1400 | { | ||
| 1401 | nLastWarned = GetTime(); | ||
| 1402 | printf("WARNING: %d peers but select() can only watch %d -- %d are not being read\n", | ||
| 1403 | (int)vNodes.size(), (int)FD_SETSIZE - 1, | ||
| 1404 | (int)vNodes.size() - (int)FD_SETSIZE + 1); | ||
| 1405 | } | ||
| 1406 | } | ||
| 1407 | nPeersWatched = (int)nWatched - 1; // less the listen socket | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | // Say the state of the node out loud now and then, so a log pulled off | ||
| 1411 | // a machine three weeks later still answers "was it hearing anybody". | ||
| 1412 | { | ||
| 1413 | static int64 nLastReport = 0; | ||
| 1414 | // Wound forward on the first pass so the first report lands about a | ||
| 1415 | // minute in, once peers have had time to connect. A report written | ||
| 1416 | // at second zero says nothing, and waiting ten minutes for the | ||
| 1417 | // first one is ten minutes of a log that cannot answer anything. | ||
| 1418 | if (nLastReport == 0) | ||
| 1419 | nLastReport = GetTime() - 9 * 60; | ||
| 1420 | if (GetTime() - nLastReport > 10 * 60) | ||
| 1421 | { | ||
| 1422 | nLastReport = GetTime(); | ||
| 1423 | printf("%s\n", GetDiagnosticsText().c_str()); | ||
| 1424 | } | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | vfThreadRunning[0] = false; | ||
| 1428 | int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); | ||
| 1429 | vfThreadRunning[0] = true; | ||
| 1430 | CheckForShutdown(0); | ||
| 1431 | if (nSelect == SOCKET_ERROR) | ||
| 1432 | { | ||
| 1433 | // This used to answer a failed select() by marking every | ||
| 1434 | // descriptor from 0 to hSocketMax ready, which is code written for | ||
| 1435 | // POSIX, where a descriptor is a small integer. A Windows SOCKET is | ||
| 1436 | // a kernel handle in the thousands, so the loop iterated over | ||
| 1437 | // numbers that are not sockets and told the rest of the function | ||
| 1438 | // that every peer had data. One bad handle in the set makes | ||
| 1439 | // select() fail on every pass, so a single closed socket put the | ||
| 1440 | // whole thread into that state permanently. | ||
| 1441 | // | ||
| 1442 | // Find the bad handle instead and let it be dropped. | ||
| 1443 | int nErr = WSAGetLastError(); | ||
| 1444 | LogPrint("net", "select failed: %d\n", nErr); | ||
| 1445 | FD_ZERO(&fdsetRecv); | ||
| 1446 | FD_ZERO(&fdsetSend); | ||
| 1447 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1448 | { | ||
| 1449 | foreach(CNode* pnode, vNodes) | ||
| 1450 | { | ||
| 1451 | // Already disconnected: its handle is gone on purpose, and | ||
| 1452 | // reporting it here would name the wrong node for a select() | ||
| 1453 | // failure it did not cause. | ||
| 1454 | if (pnode->hSocket == INVALID_SOCKET) | ||
| 1455 | continue; | ||
| 1456 | int nType = 0; | ||
| 1457 | #ifdef _WIN32 | ||
| 1458 | int nTypeLen = sizeof(nType); | ||
| 1459 | #else | ||
| 1460 | socklen_t nTypeLen = sizeof(nType); | ||
| 1461 | #endif | ||
| 1462 | if (getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0) | ||
| 1463 | { | ||
| 1464 | printf("dropping node %s: its socket is no longer valid\n", | ||
| 1465 | pnode->addr.ToString().c_str()); | ||
| 1466 | pnode->fDisconnect = true; | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | Sleep(timeout.tv_usec/1000); | ||
| 1471 | } | ||
| 1472 | RandAddSeed(); | ||
| 1473 | |||
| 1474 | //// debug print | ||
| 1475 | //foreach(CNode* pnode, vNodes) | ||
| 1476 | //{ | ||
| 1477 | // printf("vRecv = %-5d ", pnode->vRecv.size()); | ||
| 1478 | // printf("vSend = %-5d ", pnode->vSend.size()); | ||
| 1479 | //} | ||
| 1480 | //printf("\n"); | ||
| 1481 | |||
| 1482 | |||
| 1483 | // | ||
| 1484 | // Accept new connections | ||
| 1485 | // | ||
| 1486 | if (FD_ISSET(hListenSocket, &fdsetRecv)) | ||
| 1487 | { | ||
| 1488 | struct sockaddr_in sockaddr; | ||
| 1489 | #ifdef _WIN32 | ||
| 1490 | int len = sizeof(sockaddr); | ||
| 1491 | #else | ||
| 1492 | socklen_t len = sizeof(sockaddr); | ||
| 1493 | #endif | ||
| 1494 | SOCKET hSocket = BtfSocketTag(accept(hListenSocket, (struct sockaddr*)&sockaddr, &len), SOCK_ACCEPT); | ||
| 1495 | CAddress addr(sockaddr); | ||
| 1496 | if (hSocket == INVALID_SOCKET) | ||
| 1497 | { | ||
| 1498 | if (WSAGetLastError() != WSAEWOULDBLOCK) | ||
| 1499 | printf("ERROR ThreadSocketHandler accept failed: %d\n", WSAGetLastError()); | ||
| 1500 | } | ||
| 1501 | else | ||
| 1502 | { | ||
| 1503 | // Refuse rather than accept a connection this node cannot | ||
| 1504 | // watch. Without this the listen socket kept taking peers -- | ||
| 1505 | // it is always in the select set -- while everything past the | ||
| 1506 | // limit sat open and unread, which fed on itself: the deafer | ||
| 1507 | // the node got, the more connections it collected. | ||
| 1508 | unsigned int nNodes = 0; | ||
| 1509 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1510 | nNodes = (unsigned int)vNodes.size(); | ||
| 1511 | if (nNodes >= MAX_CONNECTIONS) | ||
| 1512 | { | ||
| 1513 | LogPrint("net", "refusing connection from %s, already at %u\n", | ||
| 1514 | addr.ToString().c_str(), nNodes); | ||
| 1515 | BtfCloseSocket(hSocket); | ||
| 1516 | } | ||
| 1517 | else | ||
| 1518 | { | ||
| 1519 | LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str()); | ||
| 1520 | CNode* pnode = new CNode(hSocket, addr, true); | ||
| 1521 | pnode->AddRef(); | ||
| 1522 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1523 | vNodes.push_back(pnode); | ||
| 1524 | } | ||
| 1525 | } | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | |||
| 1529 | // | ||
| 1530 | // Service each socket | ||
| 1531 | // | ||
| 1532 | vector<CNode*> vNodesCopy; | ||
| 1533 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1534 | vNodesCopy = vNodes; | ||
| 1535 | foreach(CNode* pnode, vNodesCopy) | ||
| 1536 | { | ||
| 1537 | CheckForShutdown(0); | ||
| 1538 | SOCKET hSocket = pnode->hSocket; | ||
| 1539 | |||
| 1540 | // | ||
| 1541 | // Receive | ||
| 1542 | // | ||
| 1543 | if (FD_ISSET(hSocket, &fdsetRecv)) | ||
| 1544 | { | ||
| 1545 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | ||
| 1546 | { | ||
| 1547 | CDataStream& vRecv = pnode->vRecv; | ||
| 1548 | unsigned int nPos = vRecv.size(); | ||
| 1549 | |||
| 1550 | // typical socket buffer is 8K-64K | ||
| 1551 | const unsigned int nBufSize = 0x10000; | ||
| 1552 | vRecv.resize(nPos + nBufSize); | ||
| 1553 | int nBytes = recv(hSocket, &vRecv[nPos], nBufSize, 0); | ||
| 1554 | vRecv.resize(nPos + max(nBytes, 0)); | ||
| 1555 | if (nBytes > 0) | ||
| 1556 | { | ||
| 1557 | pnode->nLastRecv = GetTime(); | ||
| 1558 | } | ||
| 1559 | else if (nBytes == 0) | ||
| 1560 | { | ||
| 1561 | // socket closed gracefully | ||
| 1562 | if (!pnode->fDisconnect) | ||
| 1563 | LogPrint("net", "recv: socket closed\n"); | ||
| 1564 | pnode->fDisconnect = true; | ||
| 1565 | } | ||
| 1566 | else if (nBytes < 0) | ||
| 1567 | { | ||
| 1568 | // socket error | ||
| 1569 | int nErr = WSAGetLastError(); | ||
| 1570 | if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) | ||
| 1571 | { | ||
| 1572 | if (!pnode->fDisconnect) | ||
| 1573 | LogPrint("net", "recv failed: %d\n", nErr); | ||
| 1574 | pnode->fDisconnect = true; | ||
| 1575 | } | ||
| 1576 | } | ||
| 1577 | } | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | // | ||
| 1581 | // Send | ||
| 1582 | // | ||
| 1583 | if (FD_ISSET(hSocket, &fdsetSend)) | ||
| 1584 | { | ||
| 1585 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | ||
| 1586 | { | ||
| 1587 | CDataStream& vSend = pnode->vSend; | ||
| 1588 | if (!vSend.empty()) | ||
| 1589 | { | ||
| 1590 | int nBytes = send(hSocket, &vSend[0], vSend.size(), 0); | ||
| 1591 | if (nBytes > 0) | ||
| 1592 | { | ||
| 1593 | vSend.erase(vSend.begin(), vSend.begin() + nBytes); | ||
| 1594 | pnode->nLastSend = GetTime(); | ||
| 1595 | } | ||
| 1596 | else if (nBytes == 0) | ||
| 1597 | { | ||
| 1598 | if (pnode->ReadyToDisconnect()) | ||
| 1599 | pnode->vSend.clear(); | ||
| 1600 | } | ||
| 1601 | else | ||
| 1602 | { | ||
| 1603 | LogPrint("net", "send error %d\n", nBytes); | ||
| 1604 | if (pnode->ReadyToDisconnect()) | ||
| 1605 | pnode->vSend.clear(); | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | if (vSend.empty()) | ||
| 1609 | pnode->nLastSendEmpty = GetTime(); | ||
| 1610 | } | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | // | ||
| 1614 | // Inactivity | ||
| 1615 | // | ||
| 1616 | // Dropping the connection is the whole cure: everything above this | ||
| 1617 | // layer already knows how to reconnect, and a peer we cannot hear | ||
| 1618 | // is worth exactly as much as no peer at all. Give a new | ||
| 1619 | // connection a grace period first, or we would cut off peers that | ||
| 1620 | // are still completing the rendezvous handshake. | ||
| 1621 | // | ||
| 1622 | if (GetTime() - pnode->nTimeConnected > BTF_HANDSHAKE_GRACE_SECS) | ||
| 1623 | { | ||
| 1624 | if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) | ||
| 1625 | { | ||
| 1626 | if (!pnode->strBtfMeeting.empty()) | ||
| 1627 | BtfChurnNoteHandshakeTimeout(pnode->strBtfAddr, pnode->strBtfMeeting, | ||
| 1628 | pnode->nLastRecv != 0, pnode->nLastSend != 0); | ||
| 1629 | LogPrint("net", "socket no message in first %d seconds, recv=%d send=%d\n", | ||
| 1630 | BTF_HANDSHAKE_GRACE_SECS, pnode->nLastRecv != 0, pnode->nLastSend != 0); | ||
| 1631 | pnode->fDisconnect = true; | ||
| 1632 | } | ||
| 1633 | else if (GetTime() - pnode->nLastSend > BTF_SEND_STALL_SECS && | ||
| 1634 | GetTime() - pnode->nLastSendEmpty > BTF_SEND_STALL_SECS) | ||
| 1635 | { | ||
| 1636 | // We have had something queued to send for this long and | ||
| 1637 | // none of it has gone out: the socket accepts no more. | ||
| 1638 | LogPrint("net", "socket not sending\n"); | ||
| 1639 | pnode->fDisconnect = true; | ||
| 1640 | } | ||
| 1641 | else if (GetTime() - pnode->nLastRecv > BTF_RECV_TIMEOUT_SECS) | ||
| 1642 | { | ||
| 1643 | // The deaf case. Nothing has arrived for many block | ||
| 1644 | // intervals while the socket still looks perfectly fine. | ||
| 1645 | LogPrint("net", "socket inactivity timeout\n"); | ||
| 1646 | pnode->fDisconnect = true; | ||
| 1647 | } | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | |||
| 1652 | Sleep(10); | ||
| 1653 | } | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | |||
| 1657 | |||
| 1658 | |||
| 1659 | |||
| 1660 | |||
| 1661 | |||
| 1662 | |||
| 1663 | |||
| 1664 | |||
| 1665 | |||
| 1666 | |||
| 1667 | |||
| 1668 | |||
| 1669 | |||
| 1670 | |||
| 1671 | |||
| 1672 | |||
| 1673 | void ThreadMessageHandler(void* parg) | ||
| 1674 | { | ||
| 1675 | IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); | ||
| 1676 | |||
| 1677 | loop | ||
| 1678 | { | ||
| 1679 | vfThreadRunning[2] = true; | ||
| 1680 | CheckForShutdown(2); | ||
| 1681 | try | ||
| 1682 | { | ||
| 1683 | ThreadMessageHandler2(parg); | ||
| 1684 | } | ||
| 1685 | CATCH_PRINT_EXCEPTION("ThreadMessageHandler()") | ||
| 1686 | vfThreadRunning[2] = false; | ||
| 1687 | Sleep(5000); | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | int GetPeerMedianHeight() | ||
| 1692 | { | ||
| 1693 | vector<int> vHeights; | ||
| 1694 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1695 | foreach(CNode* pnode, vNodes) | ||
| 1696 | if (pnode->nStartingHeight >= 0) | ||
| 1697 | vHeights.push_back(pnode->nStartingHeight); | ||
| 1698 | |||
| 1699 | if (vHeights.empty()) | ||
| 1700 | return -1; | ||
| 1701 | |||
| 1702 | sort(vHeights.begin(), vHeights.end()); | ||
| 1703 | return vHeights[vHeights.size() / 2]; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | |||
| 1707 | // Say out loud when we are behind the network. | ||
| 1708 | // | ||
| 1709 | // This is the whole reason the height is in the handshake. Every serious bug | ||
| 1710 | // this node has had looked identical from the outside: running, threads alive, | ||
| 1711 | // nothing in the log, and no blocks arriving. A node that knows where everyone | ||
| 1712 | // else is can say so, and "8 blocks behind for 27 minutes" is a sentence a | ||
| 1713 | // user can act on. Deliberately not behind a debug category -- an operator who | ||
| 1714 | // already suspects trouble is not the one who needs telling. | ||
| 1715 | static void WarnIfBehind() | ||
| 1716 | { | ||
| 1717 | static int64 nLastWarned; | ||
| 1718 | static int64 nBehindSince; | ||
| 1719 | |||
| 1720 | int nPeers = GetPeerMedianHeight(); | ||
| 1721 | if (nPeers < 0 || nBestHeight < 0) | ||
| 1722 | return; | ||
| 1723 | |||
| 1724 | // One block of slack: somebody is always mid-relay. | ||
| 1725 | if (nBestHeight >= nPeers - 1) | ||
| 1726 | { | ||
| 1727 | nBehindSince = 0; | ||
| 1728 | return; | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | int64 nNow = GetTime(); | ||
| 1732 | if (nBehindSince == 0) | ||
| 1733 | { | ||
| 1734 | nBehindSince = nNow; | ||
| 1735 | return; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | // Falling briefly behind is ordinary. Staying behind is not. | ||
| 1739 | if (nNow - nBehindSince < BTF_BEHIND_GRACE_SECS) | ||
| 1740 | return; | ||
| 1741 | if (nNow - nLastWarned < BTF_BEHIND_WARN_INTERVAL_SECS) | ||
| 1742 | return; | ||
| 1743 | |||
| 1744 | nLastWarned = nNow; | ||
| 1745 | printf("WARNING: %d blocks behind the network (height %d, peers report %d) for %d minutes\n", | ||
| 1746 | nPeers - nBestHeight, nBestHeight, nPeers, (int)((nNow - nBehindSince) / 60)); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | |||
| 1750 | void ThreadMessageHandler2(void* parg) | ||
| 1751 | { | ||
| 1752 | printf("ThreadMessageHandler started\n"); | ||
| 1753 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); | ||
| 1754 | loop | ||
| 1755 | { | ||
| 1756 | // Poll the connected nodes for messages | ||
| 1757 | vector<CNode*> vNodesCopy; | ||
| 1758 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1759 | vNodesCopy = vNodes; | ||
| 1760 | foreach(CNode* pnode, vNodesCopy) | ||
| 1761 | { | ||
| 1762 | pnode->AddRef(); | ||
| 1763 | |||
| 1764 | // Receive messages | ||
| 1765 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | ||
| 1766 | ProcessMessages(pnode); | ||
| 1767 | |||
| 1768 | // Send messages | ||
| 1769 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | ||
| 1770 | SendMessages(pnode); | ||
| 1771 | |||
| 1772 | pnode->Release(); | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | WarnIfBehind(); | ||
| 1776 | |||
| 1777 | // Wait and allow messages to bunch up | ||
| 1778 | vfThreadRunning[2] = false; | ||
| 1779 | Sleep(100); | ||
| 1780 | vfThreadRunning[2] = true; | ||
| 1781 | CheckForShutdown(2); | ||
| 1782 | } | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | |||
| 1786 | |||
| 1787 | |||
| 1788 | |||
| 1789 | |||
| 1790 | |||
| 1791 | |||
| 1792 | |||
| 1793 | // Satoshi's "todo: start one thread per processor" stood here since 2009 and is | ||
| 1794 | // what MinerThreadCount() and StartMinerThreads() below finally do -- by way of | ||
| 1795 | // std::thread::hardware_concurrency rather than NUMBER_OF_PROCESSORS, which only | ||
| 1796 | // ever existed on Windows. | ||
| 1797 | // | ||
| 1798 | // Miners share slot 3 of vfThreadRunning, and StopNode waits for every slot to | ||
| 1799 | // go false before the node tears the database down. With one miner that was the | ||
| 1800 | // same thing as "the miner finished"; with several, the first one out would | ||
| 1801 | // announce that everybody had finished while the rest were still hashing and | ||
| 1802 | // still calling ProcessBlock. So count them, and let only the last one leaving | ||
| 1803 | // clear the flag. | ||
| 1804 | static CCriticalSection cs_nMinersRunning; | ||
| 1805 | static int nMinersRunning = 0; | ||
| 1806 | |||
| 1807 | static int MinersRunningCount() | ||
| 1808 | { | ||
| 1809 | int n = 0; | ||
| 1810 | CRITICAL_BLOCK(cs_nMinersRunning) | ||
| 1811 | n = nMinersRunning; | ||
| 1812 | return n; | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | void ThreadBitcoinMiner(void* parg) | ||
| 1816 | { | ||
| 1817 | int nThreadId = (int)(intptr_t)parg; | ||
| 1818 | CRITICAL_BLOCK(cs_nMinersRunning) | ||
| 1819 | { | ||
| 1820 | nMinersRunning++; | ||
| 1821 | vfThreadRunning[3] = true; | ||
| 1822 | } | ||
| 1823 | try | ||
| 1824 | { | ||
| 1825 | bool fRet = BitcoinMiner(nThreadId); | ||
| 1826 | printf("BitcoinMiner thread %d returned %s\n", nThreadId, fRet ? "true" : "false"); | ||
| 1827 | } | ||
| 1828 | CATCH_PRINT_EXCEPTION("BitcoinMiner()") | ||
| 1829 | CRITICAL_BLOCK(cs_nMinersRunning) | ||
| 1830 | { | ||
| 1831 | if (--nMinersRunning <= 0) | ||
| 1832 | { | ||
| 1833 | nMinersRunning = 0; | ||
| 1834 | vfThreadRunning[3] = false; | ||
| 1835 | } | ||
| 1836 | } | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | // Start the miners. Returns how many actually got off the ground. | ||
| 1840 | int StartMinerThreads() | ||
| 1841 | { | ||
| 1842 | int nThreads = MinerThreadCount(); | ||
| 1843 | int nStarted = 0; | ||
| 1844 | for (int i = 0; i < nThreads; i++) | ||
| 1845 | { | ||
| 1846 | if (_beginthread(ThreadBitcoinMiner, 0, (void*)(intptr_t)(i + 1)) == (uintptr_t)-1) | ||
| 1847 | printf("Error: _beginthread(ThreadBitcoinMiner) failed on thread %d\n", i + 1); | ||
| 1848 | else | ||
| 1849 | nStarted++; | ||
| 1850 | } | ||
| 1851 | // The dataset is one allocation shared by all of them; only the 2 MB | ||
| 1852 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | ||
| 1853 | // question anyone asks. | ||
| 1854 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 1855 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1856 | nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); | ||
| 1857 | return nStarted; | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | |||
| 1861 | |||
| 1862 | |||
| 1863 | |||
| 1864 | |||
| 1865 | |||
| 1866 | |||
| 1867 | |||
| 1868 | |||
| 1869 | |||
| 1870 | bool StartNode(string& strError) | ||
| 1871 | { | ||
| 1872 | strError = ""; | ||
| 1873 | nNodeStartTime = GetTime(); | ||
| 1874 | |||
| 1875 | // Sockets startup | ||
| 1876 | WSADATA wsadata; | ||
| 1877 | int ret = WSAStartup(MAKEWORD(2,2), &wsadata); | ||
| 1878 | if (ret != NO_ERROR) | ||
| 1879 | { | ||
| 1880 | strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret); | ||
| 1881 | printf("%s\n", strError.c_str()); | ||
| 1882 | return false; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | // Get local host ip | ||
| 1886 | char pszHostName[255]; | ||
| 1887 | if (gethostname(pszHostName, 255) == SOCKET_ERROR) | ||
| 1888 | { | ||
| 1889 | strError = strprintf("Error: Unable to get IP address of this computer (gethostname returned error %d)", WSAGetLastError()); | ||
| 1890 | printf("%s\n", strError.c_str()); | ||
| 1891 | return false; | ||
| 1892 | } | ||
| 1893 | struct hostent* pHostEnt = gethostbyname(pszHostName); | ||
| 1894 | if (!pHostEnt) | ||
| 1895 | { | ||
| 1896 | strError = strprintf("Error: Unable to get IP address of this computer (gethostbyname returned error %d)", WSAGetLastError()); | ||
| 1897 | printf("%s\n", strError.c_str()); | ||
| 1898 | return false; | ||
| 1899 | } | ||
| 1900 | addrLocalHost = CAddress(*(long*)(pHostEnt->h_addr_list[0]), | ||
| 1901 | nListenPort, | ||
| 1902 | nLocalServices); | ||
| 1903 | printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); | ||
| 1904 | |||
| 1905 | // Create socket for listening for incoming connections | ||
| 1906 | SOCKET hListenSocket = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_LISTEN); | ||
| 1907 | if (hListenSocket == INVALID_SOCKET) | ||
| 1908 | { | ||
| 1909 | strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); | ||
| 1910 | printf("%s\n", strError.c_str()); | ||
| 1911 | return false; | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | // Set to nonblocking, incoming connections will also inherit this | ||
| 1915 | u_long nOne = 1; | ||
| 1916 | if (ioctlsocket(hListenSocket, FIONBIO, &nOne) == SOCKET_ERROR) | ||
| 1917 | { | ||
| 1918 | strError = strprintf("Error: Couldn't set properties on socket for incoming connections (ioctlsocket returned error %d)", WSAGetLastError()); | ||
| 1919 | printf("%s\n", strError.c_str()); | ||
| 1920 | return false; | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | // The sockaddr_in structure specifies the address family, | ||
| 1924 | // IP address, and port for the socket that is being bound | ||
| 1925 | int nRetryLimit = 15; | ||
| 1926 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); | ||
| 1927 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | ||
| 1928 | { | ||
| 1929 | int nErr = WSAGetLastError(); | ||
| 1930 | if (nErr == WSAEADDRINUSE) | ||
| 1931 | strError = strprintf("Error: Unable to bind to port %s on this computer. The program is probably already running.", addrLocalHost.ToString().c_str()); | ||
| 1932 | else | ||
| 1933 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); | ||
| 1934 | printf("%s\n", strError.c_str()); | ||
| 1935 | return false; | ||
| 1936 | } | ||
| 1937 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); | ||
| 1938 | |||
| 1939 | // Listen for incoming connections | ||
| 1940 | if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) | ||
| 1941 | { | ||
| 1942 | strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); | ||
| 1943 | printf("%s\n", strError.c_str()); | ||
| 1944 | return false; | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | // Get our external IP in a background thread -- up to 4 HTTP requests with | ||
| 1948 | // 5s timeouts each could otherwise delay startup by 20s on first run. | ||
| 1949 | // addrIncoming from a previous session is used immediately as a fallback. | ||
| 1950 | if (addrIncoming.ip) | ||
| 1951 | addrLocalHost.ip = addrIncoming.ip; | ||
| 1952 | |||
| 1953 | _beginthread([](void*) { | ||
| 1954 | unsigned int ip = addrLocalHost.ip; | ||
| 1955 | if (GetMyExternalIP(ip)) { | ||
| 1956 | addrLocalHost.ip = ip; | ||
| 1957 | addrIncoming = addrLocalHost; | ||
| 1958 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | ||
| 1959 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); | ||
| 1960 | } | ||
| 1961 | }, 0, NULL); | ||
| 1962 | |||
| 1963 | // Peers that answered last time, dialled straight away. The relays are the | ||
| 1964 | // only way to find anyone otherwise, and reaching them plus walking their | ||
| 1965 | // descriptor list is what makes a restart take minutes. | ||
| 1966 | if (_beginthread(ThreadReconnectCachedBtfPeers, 0, NULL) == -1) | ||
| 1967 | printf("Error: _beginthread(ThreadReconnectCachedBtfPeers) failed\n"); | ||
| 1968 | |||
| 1969 | // Peer discovery over Nostr relays (replaces the old IRC seed) | ||
| 1970 | if (_beginthread(ThreadNostrSeed, 0, NULL) == -1) | ||
| 1971 | printf("Error: _beginthread(ThreadNostrSeed) failed\n"); | ||
| 1972 | |||
| 1973 | // Anonymous inbound: this node's .btf hidden service, reachable through the | ||
| 1974 | // meeting relay without exposing our IP or needing a public port. | ||
| 1975 | // | ||
| 1976 | // Several of these, not one. Each holds its own registration at a | ||
| 1977 | // rendezvous, and a registration is consumed the moment a caller is paired | ||
| 1978 | // with it -- so with a single thread the node is unreachable for the whole | ||
| 1979 | // gap between being paired and registering again. Measured from a node's | ||
| 1980 | // own counters: half of all outbound dials failed for exactly that reason, | ||
| 1981 | // 15 of 30 in eleven minutes, while descriptors resolved 53 times out of | ||
| 1982 | // 54. Spares parked at the relay close that gap. | ||
| 1983 | // | ||
| 1984 | // Needs a relay that keeps more than one registration per node; against an | ||
| 1985 | // older relay the extra threads are harmless, because it drops the previous | ||
| 1986 | // registration on each new one and the node ends up where it started. | ||
| 1987 | if (!vBtfMeetingRelays.empty()) | ||
| 1988 | for (int i = 0; i < BTF_ACCEPT_THREADS; i++) | ||
| 1989 | if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) | ||
| 1990 | printf("Error: _beginthread(ThreadBtfAccept) failed\n"); | ||
| 1991 | |||
| 1992 | // Anonymous outbound: keep a connection to a specific .btf peer, if asked. | ||
| 1993 | if (!strBtfConnect.empty()) | ||
| 1994 | if (_beginthread(ThreadBtfConnect, 0, new string(strBtfConnect)) == -1) | ||
| 1995 | printf("Error: _beginthread(ThreadBtfConnect) failed\n"); | ||
| 1996 | |||
| 1997 | // | ||
| 1998 | // Start threads | ||
| 1999 | // | ||
| 2000 | if (_beginthread(ThreadSocketHandler, 0, new SOCKET(hListenSocket)) == -1) | ||
| 2001 | { | ||
| 2002 | strError = "Error: _beginthread(ThreadSocketHandler) failed"; | ||
| 2003 | printf("%s\n", strError.c_str()); | ||
| 2004 | return false; | ||
| 2005 | } | ||
| 2006 | |||
| 2007 | if (_beginthread(ThreadMessageHandler, 0, NULL) == -1) | ||
| 2008 | { | ||
| 2009 | strError = "Error: _beginthread(ThreadMessageHandler) failed"; | ||
| 2010 | printf("%s\n", strError.c_str()); | ||
| 2011 | return false; | ||
| 2012 | } | ||
| 2013 | |||
| 2014 | return true; | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | bool StopNode() | ||
| 2018 | { | ||
| 2019 | printf("StopNode()\n"); | ||
| 2020 | fShutdown = true; | ||
| 2021 | nTransactionsUpdated++; | ||
| 2022 | while (count(vfThreadRunning.begin(), vfThreadRunning.end(), true)) | ||
| 2023 | Sleep(10); | ||
| 2024 | Sleep(50); | ||
| 2025 | |||
| 2026 | // Sockets shutdown | ||
| 2027 | WSACleanup(); | ||
| 2028 | return true; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | void CheckForShutdown(int n) | ||
| 2032 | { | ||
| 2033 | if (fShutdown) | ||
| 2034 | { | ||
| 2035 | if (n != -1) | ||
| 2036 | vfThreadRunning[n] = false; | ||
| 2037 | _endthread(); | ||
| 2038 | } | ||
| 2039 | } | ||