diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 244 |
1 files changed, 128 insertions, 116 deletions
diff --git a/src/net.cpp b/src/net.cpp index 287a4bd..0576402 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -521,28 +521,9 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 521 | 521 | ||
| 522 | 522 | ||
| 523 | 523 | ||
| 524 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | 524 | // AbandonRequests() lived here, sweeping CNode::mapRequests for a callback a |
| 525 | { | 525 | // closing dialog had registered. Nothing ever registered one and nothing ever |
| 526 | // If the dialog might get closed before the reply comes back, | 526 | // called this, so it went with CRequestTracker. |
| 527 | // call this in the destructor so it doesn't get called after it's deleted. | ||
| 528 | CRITICAL_BLOCK(cs_vNodes) | ||
| 529 | { | ||
| 530 | foreach(CNode* pnode, vNodes) | ||
| 531 | { | ||
| 532 | CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 533 | { | ||
| 534 | for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) | ||
| 535 | { | ||
| 536 | CRequestTracker& tracker = (*mi).second; | ||
| 537 | if (tracker.fn == fn && tracker.param1 == param1) | ||
| 538 | pnode->mapRequests.erase(mi++); | ||
| 539 | else | ||
| 540 | mi++; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | } | ||
| 544 | } | ||
| 545 | } | ||
| 546 | 527 | ||
| 547 | 528 | ||
| 548 | 529 | ||
| @@ -550,25 +531,10 @@ void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | |||
| 550 | 531 | ||
| 551 | 532 | ||
| 552 | 533 | ||
| 553 | // | 534 | // The broadcast/subscribe channel system (AnySubscribed, CNode::IsSubscribed, |
| 554 | // Subscription methods for the broadcast and subscription system. | 535 | // Subscribe, CancelSubscribe) stood here. Its channels were MSG_TABLE and |
| 555 | // Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. | 536 | // MSG_PRODUCT, there was never an inbound handler for "subscribe", |
| 556 | // | 537 | // "sub-cancel", "publish" or "pub-cancel", and the objects it carried are gone. |
| 557 | // The subscription system uses a meet-in-the-middle strategy. | ||
| 558 | // With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers | ||
| 559 | // subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. | ||
| 560 | // | ||
| 561 | |||
| 562 | bool AnySubscribed(unsigned int nChannel) | ||
| 563 | { | ||
| 564 | if (pnodeLocalHost->IsSubscribed(nChannel)) | ||
| 565 | return true; | ||
| 566 | CRITICAL_BLOCK(cs_vNodes) | ||
| 567 | foreach(CNode* pnode, vNodes) | ||
| 568 | if (pnode->IsSubscribed(nChannel)) | ||
| 569 | return true; | ||
| 570 | return false; | ||
| 571 | } | ||
| 572 | 538 | ||
| 573 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | 539 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) |
| 574 | { | 540 | { |
| @@ -581,55 +547,6 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | |||
| 581 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); | 547 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); |
| 582 | } | 548 | } |
| 583 | 549 | ||
| 584 | bool CNode::IsSubscribed(unsigned int nChannel) | ||
| 585 | { | ||
| 586 | if (nChannel >= vfSubscribe.size()) | ||
| 587 | return false; | ||
| 588 | return vfSubscribe[nChannel]; | ||
| 589 | } | ||
| 590 | |||
| 591 | void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) | ||
| 592 | { | ||
| 593 | if (nChannel >= vfSubscribe.size()) | ||
| 594 | return; | ||
| 595 | |||
| 596 | if (!AnySubscribed(nChannel)) | ||
| 597 | { | ||
| 598 | // Relay subscribe | ||
| 599 | CRITICAL_BLOCK(cs_vNodes) | ||
| 600 | foreach(CNode* pnode, vNodes) | ||
| 601 | if (pnode != this) | ||
| 602 | pnode->PushMessage("subscribe", nChannel, nHops); | ||
| 603 | } | ||
| 604 | |||
| 605 | vfSubscribe[nChannel] = true; | ||
| 606 | } | ||
| 607 | |||
| 608 | void CNode::CancelSubscribe(unsigned int nChannel) | ||
| 609 | { | ||
| 610 | if (nChannel >= vfSubscribe.size()) | ||
| 611 | return; | ||
| 612 | |||
| 613 | // Prevent from relaying cancel if wasn't subscribed | ||
| 614 | if (!vfSubscribe[nChannel]) | ||
| 615 | return; | ||
| 616 | vfSubscribe[nChannel] = false; | ||
| 617 | |||
| 618 | if (!AnySubscribed(nChannel)) | ||
| 619 | { | ||
| 620 | // Relay subscription cancel | ||
| 621 | CRITICAL_BLOCK(cs_vNodes) | ||
| 622 | foreach(CNode* pnode, vNodes) | ||
| 623 | if (pnode != this) | ||
| 624 | pnode->PushMessage("sub-cancel", nChannel); | ||
| 625 | |||
| 626 | // Clear memory, no longer subscribed | ||
| 627 | if (nChannel == MSG_PRODUCT) | ||
| 628 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 629 | mapProducts.clear(); | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | 550 | ||
| 634 | 551 | ||
| 635 | 552 | ||
| @@ -1205,6 +1122,18 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting | |||
| 1205 | void ThreadBtfAccept(void* parg) | 1122 | void ThreadBtfAccept(void* parg) |
| 1206 | { | 1123 | { |
| 1207 | printf("ThreadBtfAccept started\n"); | 1124 | printf("ThreadBtfAccept started\n"); |
| 1125 | // In operator mode the pool server registers this same node identity as a | ||
| 1126 | // stratum service at the rendezvous relays, and a relay only holds ONE | ||
| 1127 | // service registration per pubkey -- peer-accept and pool-accept would | ||
| 1128 | // kick each other off forever, breaking both. The pool's registration is | ||
| 1129 | // the one that must be stable (that's what miners resolve and dial), so | ||
| 1130 | // the node skips inbound rendezvous here and relies on outbound .btf | ||
| 1131 | // connections for block sync. | ||
| 1132 | if (nMineMode == MINE_OPERATOR) | ||
| 1133 | { | ||
| 1134 | printf("ThreadBtfAccept: operator mode -- skipping peer rendezvous registration\n"); | ||
| 1135 | return; | ||
| 1136 | } | ||
| 1208 | unsigned char pk[32]; | 1137 | unsigned char pk[32]; |
| 1209 | unsigned char sk[32]; | 1138 | unsigned char sk[32]; |
| 1210 | while (!BtfGetIdentity(pk, sk)) | 1139 | while (!BtfGetIdentity(pk, sk)) |
| @@ -1354,17 +1283,6 @@ void CNode::Disconnect() | |||
| 1354 | BtfCloseSocket(hSocket); | 1283 | BtfCloseSocket(hSocket); |
| 1355 | hSocket = INVALID_SOCKET; | 1284 | hSocket = INVALID_SOCKET; |
| 1356 | 1285 | ||
| 1357 | // All of a nodes broadcasts and subscriptions are automatically torn down | ||
| 1358 | // when it goes down, so a node has to stay up to keep its broadcast going. | ||
| 1359 | |||
| 1360 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 1361 | for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();) | ||
| 1362 | AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second); | ||
| 1363 | |||
| 1364 | // Cancel subscriptions | ||
| 1365 | for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) | ||
| 1366 | if (vfSubscribe[nChannel]) | ||
| 1367 | CancelSubscribe(nChannel); | ||
| 1368 | } | 1286 | } |
| 1369 | 1287 | ||
| 1370 | 1288 | ||
| @@ -1489,7 +1407,6 @@ void ThreadSocketHandler2(void* parg) | |||
| 1489 | bool fDelete = false; | 1407 | bool fDelete = false; |
| 1490 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1408 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1491 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | 1409 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) |
| 1492 | TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 1493 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) | 1410 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) |
| 1494 | fDelete = true; | 1411 | fDelete = true; |
| 1495 | if (fDelete) | 1412 | if (fDelete) |
| @@ -1597,6 +1514,27 @@ void ThreadSocketHandler2(void* parg) | |||
| 1597 | printf("dropping node %s: its socket is no longer valid\n", | 1514 | printf("dropping node %s: its socket is no longer valid\n", |
| 1598 | pnode->addr.ToString().c_str()); | 1515 | pnode->addr.ToString().c_str()); |
| 1599 | pnode->fDisconnect = true; | 1516 | pnode->fDisconnect = true; |
| 1517 | |||
| 1518 | // Stop referring to it, or it goes into the next poll | ||
| 1519 | // set and fails it again. | ||
| 1520 | // | ||
| 1521 | // Marking fDisconnect is not enough: a node is only | ||
| 1522 | // removed from vNodes once it has drained or once | ||
| 1523 | // DISCONNECT_DRAIN_SECS has passed, so one dead | ||
| 1524 | // descriptor kept failing the wait for a full minute -- | ||
| 1525 | // and a failed wait skips the service loop below | ||
| 1526 | // entirely, which means NO peer is read on that pass. | ||
| 1527 | // Measured on a wedged pool under the old select() | ||
| 1528 | // implementation: 1,027 consecutive passes over 61 | ||
| 1529 | // seconds, one bad socket, the entire node deaf. poll() | ||
| 1530 | // reports a bad descriptor as POLLNVAL on that entry | ||
| 1531 | // rather than failing the whole call, so this path is | ||
| 1532 | // now the backstop rather than the common case. | ||
| 1533 | // | ||
| 1534 | // Not closed, only forgotten. getsockopt just told us | ||
| 1535 | // this descriptor is not ours any more; closing it now | ||
| 1536 | // is how an unrelated socket gets closed instead. | ||
| 1537 | pnode->hSocket = INVALID_SOCKET; | ||
| 1600 | } | 1538 | } |
| 1601 | } | 1539 | } |
| 1602 | } | 1540 | } |
| @@ -1870,6 +1808,13 @@ void ThreadMessageHandler(void* parg) | |||
| 1870 | } | 1808 | } |
| 1871 | } | 1809 | } |
| 1872 | 1810 | ||
| 1811 | // When any peer last told us a height. A wedged node -- one that still has | ||
| 1812 | // peers but has stopped hearing them -- is exactly a node where this stops | ||
| 1813 | // advancing, and that is the one thing the height comparison itself cannot | ||
| 1814 | // see: there is no height to compare. | ||
| 1815 | static int64 nLastPeerHeightSeen = 0; | ||
| 1816 | static int64 nPeerHeightFirstAsked = 0; | ||
| 1817 | |||
| 1873 | int GetPeerMedianHeight() | 1818 | int GetPeerMedianHeight() |
| 1874 | { | 1819 | { |
| 1875 | vector<int> vHeights; | 1820 | vector<int> vHeights; |
| @@ -1881,10 +1826,26 @@ int GetPeerMedianHeight() | |||
| 1881 | if (vHeights.empty()) | 1826 | if (vHeights.empty()) |
| 1882 | return -1; | 1827 | return -1; |
| 1883 | 1828 | ||
| 1829 | nLastPeerHeightSeen = GetTime(); | ||
| 1884 | sort(vHeights.begin(), vHeights.end()); | 1830 | sort(vHeights.begin(), vHeights.end()); |
| 1885 | return vHeights[vHeights.size() / 2]; | 1831 | return vHeights[vHeights.size() / 2]; |
| 1886 | } | 1832 | } |
| 1887 | 1833 | ||
| 1834 | // Seconds since any peer last reported a height. | ||
| 1835 | // | ||
| 1836 | // Before ever hearing one, measure from the first time somebody asked rather | ||
| 1837 | // than from the epoch -- otherwise a node refuses jobs the instant it starts, | ||
| 1838 | // before it has had a chance to complete a single handshake. | ||
| 1839 | int64 GetPeerHeightAgeSecs() | ||
| 1840 | { | ||
| 1841 | int64 nNow = GetTime(); | ||
| 1842 | if (nPeerHeightFirstAsked == 0) | ||
| 1843 | nPeerHeightFirstAsked = nNow; | ||
| 1844 | if (nLastPeerHeightSeen == 0) | ||
| 1845 | return nNow - nPeerHeightFirstAsked; | ||
| 1846 | return nNow - nLastPeerHeightSeen; | ||
| 1847 | } | ||
| 1848 | |||
| 1888 | 1849 | ||
| 1889 | // Say out loud when we are behind the network. | 1850 | // Say out loud when we are behind the network. |
| 1890 | // | 1851 | // |
| @@ -2021,7 +1982,21 @@ void ThreadBitcoinMiner(void* parg) | |||
| 2021 | // Start the miners. Returns how many actually got off the ground. | 1982 | // Start the miners. Returns how many actually got off the ground. |
| 2022 | int StartMinerThreads() | 1983 | int StartMinerThreads() |
| 2023 | { | 1984 | { |
| 2024 | int nThreads = MinerThreadCount(); | 1985 | // MinerThreadCount() sizes the SOLO miner, where each thread hashes a |
| 1986 | // template of its own and more threads is simply more hashrate. | ||
| 1987 | // | ||
| 1988 | // Participant mode is not that shape. Every thread that enters | ||
| 1989 | // BitcoinMiner() takes the MINE_PARTICIPANT branch, and that branch is a | ||
| 1990 | // whole miner: its own Stratum session to the pool, its own payout address | ||
| 1991 | // from the rotation, and its own pool of /hashthreads worker threads. So | ||
| 1992 | // the two counts multiply instead of dividing, and the machine ends up | ||
| 1993 | // running MinerThreadCount() x nHashThreads hashing threads plus one pool | ||
| 1994 | // connection each. On a 12-core host with /hashthreads=2 that is 11 | ||
| 1995 | // sessions and 22 hash threads -- roughly 2x the cores, which is not more | ||
| 1996 | // work done, only more contention -- and with /hashthreads unset it is 11 | ||
| 1997 | // sessions of 11 workers. One session is the entirety of participant mode; | ||
| 1998 | // /hashthreads is what sizes it, and it can then mean what it says. | ||
| 1999 | int nThreads = (nMineMode == MINE_PARTICIPANT) ? 1 : MinerThreadCount(); | ||
| 2025 | int nStarted = 0; | 2000 | int nStarted = 0; |
| 2026 | for (int i = 0; i < nThreads; i++) | 2001 | for (int i = 0; i < nThreads; i++) |
| 2027 | { | 2002 | { |
| @@ -2033,9 +2008,24 @@ int StartMinerThreads() | |||
| 2033 | // The dataset is one allocation shared by all of them; only the 2 MB | 2008 | // The dataset is one allocation shared by all of them; only the 2 MB |
| 2034 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | 2009 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first |
| 2035 | // question anyone asks. | 2010 | // question anyone asks. |
| 2036 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | 2011 | // |
| 2037 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | 2012 | // Participant mode reports the workers that session will run, not the one |
| 2038 | nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); | 2013 | // thread that runs it: the hash thread count is the number an operator |
| 2014 | // watches, and it is the number that was wrong here. It is otherwise only | ||
| 2015 | // logged under -debug, which is how 11 sessions ran unnoticed. | ||
| 2016 | if (nMineMode == MINE_PARTICIPANT) | ||
| 2017 | { | ||
| 2018 | int nWorkers = ParticipantHashThreadCount(); | ||
| 2019 | printf("Mining for the pool with %d hash thread(s) of %u core(s) -- about %d MB " | ||
| 2020 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 2021 | nWorkers, AvailableCoreCount(), 2080 + 2 * nWorkers); | ||
| 2022 | } | ||
| 2023 | else | ||
| 2024 | { | ||
| 2025 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 2026 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 2027 | nStarted, AvailableCoreCount(), 2080 + 2 * nStarted); | ||
| 2028 | } | ||
| 2039 | return nStarted; | 2029 | return nStarted; |
| 2040 | } | 2030 | } |
| 2041 | 2031 | ||
| @@ -2106,20 +2096,41 @@ bool StartNode(string& strError) | |||
| 2106 | // IP address, and port for the socket that is being bound | 2096 | // IP address, and port for the socket that is being bound |
| 2107 | int nRetryLimit = 15; | 2097 | int nRetryLimit = 15; |
| 2108 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); | 2098 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); |
| 2109 | // Keep addrLocalHost as this node's advertised address, but listen on all | 2099 | // Managed Tor forwards hidden-service traffic to 127.0.0.1:<port>, which a |
| 2110 | // local interfaces. Managed Tor forwards hidden-service traffic to | 2100 | // LAN-only bind cannot receive -- so a node that advertises an onion |
| 2111 | // 127.0.0.1:<port>, and binding only to the LAN address made that onion | 2101 | // endpoint must listen on all local interfaces. Upstream 1.2.19 bound |
| 2112 | // endpoint unreachable even though the P2P listener was running. | 2102 | // INADDR_ANY unconditionally, which also widened the P2P listener of every |
| 2113 | sockaddr.sin_addr.s_addr = INADDR_ANY; | 2103 | // non-Tor node to VPN tunnels, docker bridges and secondary NICs. Keep the |
| 2104 | // wildcard bind conditional on actually having an onion endpoint. | ||
| 2105 | if (!BtfLocalOnionEndpoint().empty()) | ||
| 2106 | sockaddr.sin_addr.s_addr = INADDR_ANY; | ||
| 2114 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | 2107 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) |
| 2115 | { | 2108 | { |
| 2116 | int nErr = WSAGetLastError(); | 2109 | int nErr = WSAGetLastError(); |
| 2117 | if (nErr == WSAEADDRINUSE) | 2110 | if (nErr == WSAEADDRINUSE) |
| 2118 | strError = strprintf("Error: Unable to bind to port %s on this computer. The program is probably already running.", addrLocalHost.ToString().c_str()); | 2111 | { |
| 2112 | // Another local instance holds the default port. Fall back to an | ||
| 2113 | // ephemeral port: inbound direct-IP peering is optional, and .btf | ||
| 2114 | // rendezvous connectivity is unaffected by the local listen port. | ||
| 2115 | sockaddr.sin_port = htons(0); | ||
| 2116 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | ||
| 2117 | { | ||
| 2118 | strError = strprintf("Error: Unable to bind any port on this computer (bind returned error %d)", WSAGetLastError()); | ||
| 2119 | printf("%s\n", strError.c_str()); | ||
| 2120 | return false; | ||
| 2121 | } | ||
| 2122 | struct sockaddr_in bound; | ||
| 2123 | socklen_t nBoundLen = sizeof(bound); | ||
| 2124 | if (getsockname(hListenSocket, (struct sockaddr*)&bound, &nBoundLen) == 0) | ||
| 2125 | addrLocalHost.port = bound.sin_port; | ||
| 2126 | printf("default port in use by another instance; bound to ephemeral %s instead\n", addrLocalHost.ToString().c_str()); | ||
| 2127 | } | ||
| 2119 | else | 2128 | else |
| 2129 | { | ||
| 2120 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); | 2130 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); |
| 2121 | printf("%s\n", strError.c_str()); | 2131 | printf("%s\n", strError.c_str()); |
| 2122 | return false; | 2132 | return false; |
| 2133 | } | ||
| 2123 | } | 2134 | } |
| 2124 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); | 2135 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); |
| 2125 | 2136 | ||
| @@ -2137,7 +2148,7 @@ bool StartNode(string& strError) | |||
| 2137 | if (addrIncoming.ip) | 2148 | if (addrIncoming.ip) |
| 2138 | addrLocalHost.ip = addrIncoming.ip; | 2149 | addrLocalHost.ip = addrIncoming.ip; |
| 2139 | 2150 | ||
| 2140 | _beginthread([](void*) { | 2151 | if (_beginthread([](void*) { |
| 2141 | unsigned int ip = addrLocalHost.ip; | 2152 | unsigned int ip = addrLocalHost.ip; |
| 2142 | if (GetMyExternalIP(ip)) { | 2153 | if (GetMyExternalIP(ip)) { |
| 2143 | addrLocalHost.ip = ip; | 2154 | addrLocalHost.ip = ip; |
| @@ -2145,7 +2156,8 @@ bool StartNode(string& strError) | |||
| 2145 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | 2156 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); |
| 2146 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); | 2157 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); |
| 2147 | } | 2158 | } |
| 2148 | }, 0, NULL); | 2159 | }, 0, NULL) == (uintptr_t)-1) |
| 2160 | printf("Error: _beginthread(GetMyExternalIP) failed\n"); | ||
| 2149 | 2161 | ||
| 2150 | // Peers that answered last time, dialled straight away. The relays are the | 2162 | // Peers that answered last time, dialled straight away. The relays are the |
| 2151 | // only way to find anyone otherwise, and reaching them plus walking their | 2163 | // only way to find anyone otherwise, and reaching them plus walking their |