diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 239 |
1 files changed, 125 insertions, 114 deletions
diff --git a/src/net.cpp b/src/net.cpp index 4603d2d..4741f09 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -492,28 +492,9 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 492 | 492 | ||
| 493 | 493 | ||
| 494 | 494 | ||
| 495 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | 495 | // AbandonRequests() lived here, sweeping CNode::mapRequests for a callback a |
| 496 | { | 496 | // closing dialog had registered. Nothing ever registered one and nothing ever |
| 497 | // If the dialog might get closed before the reply comes back, | 497 | // called this, so it went with CRequestTracker. |
| 498 | // call this in the destructor so it doesn't get called after it's deleted. | ||
| 499 | CRITICAL_BLOCK(cs_vNodes) | ||
| 500 | { | ||
| 501 | foreach(CNode* pnode, vNodes) | ||
| 502 | { | ||
| 503 | CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 504 | { | ||
| 505 | for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) | ||
| 506 | { | ||
| 507 | CRequestTracker& tracker = (*mi).second; | ||
| 508 | if (tracker.fn == fn && tracker.param1 == param1) | ||
| 509 | pnode->mapRequests.erase(mi++); | ||
| 510 | else | ||
| 511 | mi++; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | } | ||
| 517 | 498 | ||
| 518 | 499 | ||
| 519 | 500 | ||
| @@ -521,25 +502,10 @@ void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | |||
| 521 | 502 | ||
| 522 | 503 | ||
| 523 | 504 | ||
| 524 | // | 505 | // The broadcast/subscribe channel system (AnySubscribed, CNode::IsSubscribed, |
| 525 | // Subscription methods for the broadcast and subscription system. | 506 | // Subscribe, CancelSubscribe) stood here. Its channels were MSG_TABLE and |
| 526 | // Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. | 507 | // MSG_PRODUCT, there was never an inbound handler for "subscribe", |
| 527 | // | 508 | // "sub-cancel", "publish" or "pub-cancel", and the objects it carried are gone. |
| 528 | // The subscription system uses a meet-in-the-middle strategy. | ||
| 529 | // With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers | ||
| 530 | // subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. | ||
| 531 | // | ||
| 532 | |||
| 533 | bool AnySubscribed(unsigned int nChannel) | ||
| 534 | { | ||
| 535 | if (pnodeLocalHost->IsSubscribed(nChannel)) | ||
| 536 | return true; | ||
| 537 | CRITICAL_BLOCK(cs_vNodes) | ||
| 538 | foreach(CNode* pnode, vNodes) | ||
| 539 | if (pnode->IsSubscribed(nChannel)) | ||
| 540 | return true; | ||
| 541 | return false; | ||
| 542 | } | ||
| 543 | 509 | ||
| 544 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | 510 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) |
| 545 | { | 511 | { |
| @@ -552,55 +518,6 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | |||
| 552 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); | 518 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); |
| 553 | } | 519 | } |
| 554 | 520 | ||
| 555 | bool CNode::IsSubscribed(unsigned int nChannel) | ||
| 556 | { | ||
| 557 | if (nChannel >= vfSubscribe.size()) | ||
| 558 | return false; | ||
| 559 | return vfSubscribe[nChannel]; | ||
| 560 | } | ||
| 561 | |||
| 562 | void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) | ||
| 563 | { | ||
| 564 | if (nChannel >= vfSubscribe.size()) | ||
| 565 | return; | ||
| 566 | |||
| 567 | if (!AnySubscribed(nChannel)) | ||
| 568 | { | ||
| 569 | // Relay subscribe | ||
| 570 | CRITICAL_BLOCK(cs_vNodes) | ||
| 571 | foreach(CNode* pnode, vNodes) | ||
| 572 | if (pnode != this) | ||
| 573 | pnode->PushMessage("subscribe", nChannel, nHops); | ||
| 574 | } | ||
| 575 | |||
| 576 | vfSubscribe[nChannel] = true; | ||
| 577 | } | ||
| 578 | |||
| 579 | void CNode::CancelSubscribe(unsigned int nChannel) | ||
| 580 | { | ||
| 581 | if (nChannel >= vfSubscribe.size()) | ||
| 582 | return; | ||
| 583 | |||
| 584 | // Prevent from relaying cancel if wasn't subscribed | ||
| 585 | if (!vfSubscribe[nChannel]) | ||
| 586 | return; | ||
| 587 | vfSubscribe[nChannel] = false; | ||
| 588 | |||
| 589 | if (!AnySubscribed(nChannel)) | ||
| 590 | { | ||
| 591 | // Relay subscription cancel | ||
| 592 | CRITICAL_BLOCK(cs_vNodes) | ||
| 593 | foreach(CNode* pnode, vNodes) | ||
| 594 | if (pnode != this) | ||
| 595 | pnode->PushMessage("sub-cancel", nChannel); | ||
| 596 | |||
| 597 | // Clear memory, no longer subscribed | ||
| 598 | if (nChannel == MSG_PRODUCT) | ||
| 599 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 600 | mapProducts.clear(); | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | 521 | ||
| 605 | 522 | ||
| 606 | 523 | ||
| @@ -1176,6 +1093,18 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting | |||
| 1176 | void ThreadBtfAccept(void* parg) | 1093 | void ThreadBtfAccept(void* parg) |
| 1177 | { | 1094 | { |
| 1178 | printf("ThreadBtfAccept started\n"); | 1095 | printf("ThreadBtfAccept started\n"); |
| 1096 | // In operator mode the pool server registers this same node identity as a | ||
| 1097 | // stratum service at the rendezvous relays, and a relay only holds ONE | ||
| 1098 | // service registration per pubkey -- peer-accept and pool-accept would | ||
| 1099 | // kick each other off forever, breaking both. The pool's registration is | ||
| 1100 | // the one that must be stable (that's what miners resolve and dial), so | ||
| 1101 | // the node skips inbound rendezvous here and relies on outbound .btf | ||
| 1102 | // connections for block sync. | ||
| 1103 | if (nMineMode == MINE_OPERATOR) | ||
| 1104 | { | ||
| 1105 | printf("ThreadBtfAccept: operator mode -- skipping peer rendezvous registration\n"); | ||
| 1106 | return; | ||
| 1107 | } | ||
| 1179 | unsigned char pk[32]; | 1108 | unsigned char pk[32]; |
| 1180 | unsigned char sk[32]; | 1109 | unsigned char sk[32]; |
| 1181 | while (!BtfGetIdentity(pk, sk)) | 1110 | while (!BtfGetIdentity(pk, sk)) |
| @@ -1325,17 +1254,6 @@ void CNode::Disconnect() | |||
| 1325 | BtfCloseSocket(hSocket); | 1254 | BtfCloseSocket(hSocket); |
| 1326 | hSocket = INVALID_SOCKET; | 1255 | hSocket = INVALID_SOCKET; |
| 1327 | 1256 | ||
| 1328 | // All of a nodes broadcasts and subscriptions are automatically torn down | ||
| 1329 | // when it goes down, so a node has to stay up to keep its broadcast going. | ||
| 1330 | |||
| 1331 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 1332 | for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();) | ||
| 1333 | AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second); | ||
| 1334 | |||
| 1335 | // Cancel subscriptions | ||
| 1336 | for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) | ||
| 1337 | if (vfSubscribe[nChannel]) | ||
| 1338 | CancelSubscribe(nChannel); | ||
| 1339 | } | 1257 | } |
| 1340 | 1258 | ||
| 1341 | 1259 | ||
| @@ -1460,7 +1378,6 @@ void ThreadSocketHandler2(void* parg) | |||
| 1460 | bool fDelete = false; | 1378 | bool fDelete = false; |
| 1461 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1379 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1462 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | 1380 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) |
| 1463 | TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 1464 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) | 1381 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) |
| 1465 | fDelete = true; | 1382 | fDelete = true; |
| 1466 | if (fDelete) | 1383 | if (fDelete) |
| @@ -1568,6 +1485,27 @@ void ThreadSocketHandler2(void* parg) | |||
| 1568 | printf("dropping node %s: its socket is no longer valid\n", | 1485 | printf("dropping node %s: its socket is no longer valid\n", |
| 1569 | pnode->addr.ToString().c_str()); | 1486 | pnode->addr.ToString().c_str()); |
| 1570 | pnode->fDisconnect = true; | 1487 | pnode->fDisconnect = true; |
| 1488 | |||
| 1489 | // Stop referring to it, or it goes into the next poll | ||
| 1490 | // set and fails it again. | ||
| 1491 | // | ||
| 1492 | // Marking fDisconnect is not enough: a node is only | ||
| 1493 | // removed from vNodes once it has drained or once | ||
| 1494 | // DISCONNECT_DRAIN_SECS has passed, so one dead | ||
| 1495 | // descriptor kept failing the wait for a full minute -- | ||
| 1496 | // and a failed wait skips the service loop below | ||
| 1497 | // entirely, which means NO peer is read on that pass. | ||
| 1498 | // Measured on a wedged pool under the old select() | ||
| 1499 | // implementation: 1,027 consecutive passes over 61 | ||
| 1500 | // seconds, one bad socket, the entire node deaf. poll() | ||
| 1501 | // reports a bad descriptor as POLLNVAL on that entry | ||
| 1502 | // rather than failing the whole call, so this path is | ||
| 1503 | // now the backstop rather than the common case. | ||
| 1504 | // | ||
| 1505 | // Not closed, only forgotten. getsockopt just told us | ||
| 1506 | // this descriptor is not ours any more; closing it now | ||
| 1507 | // is how an unrelated socket gets closed instead. | ||
| 1508 | pnode->hSocket = INVALID_SOCKET; | ||
| 1571 | } | 1509 | } |
| 1572 | } | 1510 | } |
| 1573 | } | 1511 | } |
| @@ -1841,6 +1779,13 @@ void ThreadMessageHandler(void* parg) | |||
| 1841 | } | 1779 | } |
| 1842 | } | 1780 | } |
| 1843 | 1781 | ||
| 1782 | // When any peer last told us a height. A wedged node -- one that still has | ||
| 1783 | // peers but has stopped hearing them -- is exactly a node where this stops | ||
| 1784 | // advancing, and that is the one thing the height comparison itself cannot | ||
| 1785 | // see: there is no height to compare. | ||
| 1786 | static int64 nLastPeerHeightSeen = 0; | ||
| 1787 | static int64 nPeerHeightFirstAsked = 0; | ||
| 1788 | |||
| 1844 | int GetPeerMedianHeight() | 1789 | int GetPeerMedianHeight() |
| 1845 | { | 1790 | { |
| 1846 | vector<int> vHeights; | 1791 | vector<int> vHeights; |
| @@ -1852,10 +1797,26 @@ int GetPeerMedianHeight() | |||
| 1852 | if (vHeights.empty()) | 1797 | if (vHeights.empty()) |
| 1853 | return -1; | 1798 | return -1; |
| 1854 | 1799 | ||
| 1800 | nLastPeerHeightSeen = GetTime(); | ||
| 1855 | sort(vHeights.begin(), vHeights.end()); | 1801 | sort(vHeights.begin(), vHeights.end()); |
| 1856 | return vHeights[vHeights.size() / 2]; | 1802 | return vHeights[vHeights.size() / 2]; |
| 1857 | } | 1803 | } |
| 1858 | 1804 | ||
| 1805 | // Seconds since any peer last reported a height. | ||
| 1806 | // | ||
| 1807 | // Before ever hearing one, measure from the first time somebody asked rather | ||
| 1808 | // than from the epoch -- otherwise a node refuses jobs the instant it starts, | ||
| 1809 | // before it has had a chance to complete a single handshake. | ||
| 1810 | int64 GetPeerHeightAgeSecs() | ||
| 1811 | { | ||
| 1812 | int64 nNow = GetTime(); | ||
| 1813 | if (nPeerHeightFirstAsked == 0) | ||
| 1814 | nPeerHeightFirstAsked = nNow; | ||
| 1815 | if (nLastPeerHeightSeen == 0) | ||
| 1816 | return nNow - nPeerHeightFirstAsked; | ||
| 1817 | return nNow - nLastPeerHeightSeen; | ||
| 1818 | } | ||
| 1819 | |||
| 1859 | 1820 | ||
| 1860 | // Say out loud when we are behind the network. | 1821 | // Say out loud when we are behind the network. |
| 1861 | // | 1822 | // |
| @@ -1992,7 +1953,21 @@ void ThreadBitcoinMiner(void* parg) | |||
| 1992 | // Start the miners. Returns how many actually got off the ground. | 1953 | // Start the miners. Returns how many actually got off the ground. |
| 1993 | int StartMinerThreads() | 1954 | int StartMinerThreads() |
| 1994 | { | 1955 | { |
| 1995 | int nThreads = MinerThreadCount(); | 1956 | // MinerThreadCount() sizes the SOLO miner, where each thread hashes a |
| 1957 | // template of its own and more threads is simply more hashrate. | ||
| 1958 | // | ||
| 1959 | // Participant mode is not that shape. Every thread that enters | ||
| 1960 | // BitcoinMiner() takes the MINE_PARTICIPANT branch, and that branch is a | ||
| 1961 | // whole miner: its own Stratum session to the pool, its own payout address | ||
| 1962 | // from the rotation, and its own pool of /hashthreads worker threads. So | ||
| 1963 | // the two counts multiply instead of dividing, and the machine ends up | ||
| 1964 | // running MinerThreadCount() x nHashThreads hashing threads plus one pool | ||
| 1965 | // connection each. On a 12-core host with /hashthreads=2 that is 11 | ||
| 1966 | // sessions and 22 hash threads -- roughly 2x the cores, which is not more | ||
| 1967 | // work done, only more contention -- and with /hashthreads unset it is 11 | ||
| 1968 | // sessions of 11 workers. One session is the entirety of participant mode; | ||
| 1969 | // /hashthreads is what sizes it, and it can then mean what it says. | ||
| 1970 | int nThreads = (nMineMode == MINE_PARTICIPANT) ? 1 : MinerThreadCount(); | ||
| 1996 | int nStarted = 0; | 1971 | int nStarted = 0; |
| 1997 | for (int i = 0; i < nThreads; i++) | 1972 | for (int i = 0; i < nThreads; i++) |
| 1998 | { | 1973 | { |
| @@ -2004,9 +1979,24 @@ int StartMinerThreads() | |||
| 2004 | // The dataset is one allocation shared by all of them; only the 2 MB | 1979 | // The dataset is one allocation shared by all of them; only the 2 MB |
| 2005 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | 1980 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first |
| 2006 | // question anyone asks. | 1981 | // question anyone asks. |
| 2007 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | 1982 | // |
| 2008 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | 1983 | // Participant mode reports the workers that session will run, not the one |
| 2009 | nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); | 1984 | // thread that runs it: the hash thread count is the number an operator |
| 1985 | // watches, and it is the number that was wrong here. It is otherwise only | ||
| 1986 | // logged under -debug, which is how 11 sessions ran unnoticed. | ||
| 1987 | if (nMineMode == MINE_PARTICIPANT) | ||
| 1988 | { | ||
| 1989 | int nWorkers = ParticipantHashThreadCount(); | ||
| 1990 | printf("Mining for the pool with %d hash thread(s) of %u core(s) -- about %d MB " | ||
| 1991 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1992 | nWorkers, AvailableCoreCount(), 2080 + 2 * nWorkers); | ||
| 1993 | } | ||
| 1994 | else | ||
| 1995 | { | ||
| 1996 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 1997 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1998 | nStarted, AvailableCoreCount(), 2080 + 2 * nStarted); | ||
| 1999 | } | ||
| 2010 | return nStarted; | 2000 | return nStarted; |
| 2011 | } | 2001 | } |
| 2012 | 2002 | ||
| @@ -2077,20 +2067,41 @@ bool StartNode(string& strError) | |||
| 2077 | // IP address, and port for the socket that is being bound | 2067 | // IP address, and port for the socket that is being bound |
| 2078 | int nRetryLimit = 15; | 2068 | int nRetryLimit = 15; |
| 2079 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); | 2069 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); |
| 2080 | // Keep addrLocalHost as this node's advertised address, but listen on all | 2070 | // Managed Tor forwards hidden-service traffic to 127.0.0.1:<port>, which a |
| 2081 | // local interfaces. Managed Tor forwards hidden-service traffic to | 2071 | // LAN-only bind cannot receive -- so a node that advertises an onion |
| 2082 | // 127.0.0.1:<port>, and binding only to the LAN address made that onion | 2072 | // endpoint must listen on all local interfaces. Upstream 1.2.19 bound |
| 2083 | // endpoint unreachable even though the P2P listener was running. | 2073 | // INADDR_ANY unconditionally, which also widened the P2P listener of every |
| 2084 | sockaddr.sin_addr.s_addr = INADDR_ANY; | 2074 | // non-Tor node to VPN tunnels, docker bridges and secondary NICs. Keep the |
| 2075 | // wildcard bind conditional on actually having an onion endpoint. | ||
| 2076 | if (!BtfLocalOnionEndpoint().empty()) | ||
| 2077 | sockaddr.sin_addr.s_addr = INADDR_ANY; | ||
| 2085 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | 2078 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) |
| 2086 | { | 2079 | { |
| 2087 | int nErr = WSAGetLastError(); | 2080 | int nErr = WSAGetLastError(); |
| 2088 | if (nErr == WSAEADDRINUSE) | 2081 | if (nErr == WSAEADDRINUSE) |
| 2089 | strError = strprintf("Error: Unable to bind to port %s on this computer. The program is probably already running.", addrLocalHost.ToString().c_str()); | 2082 | { |
| 2083 | // Another local instance holds the default port. Fall back to an | ||
| 2084 | // ephemeral port: inbound direct-IP peering is optional, and .btf | ||
| 2085 | // rendezvous connectivity is unaffected by the local listen port. | ||
| 2086 | sockaddr.sin_port = htons(0); | ||
| 2087 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | ||
| 2088 | { | ||
| 2089 | strError = strprintf("Error: Unable to bind any port on this computer (bind returned error %d)", WSAGetLastError()); | ||
| 2090 | printf("%s\n", strError.c_str()); | ||
| 2091 | return false; | ||
| 2092 | } | ||
| 2093 | struct sockaddr_in bound; | ||
| 2094 | socklen_t nBoundLen = sizeof(bound); | ||
| 2095 | if (getsockname(hListenSocket, (struct sockaddr*)&bound, &nBoundLen) == 0) | ||
| 2096 | addrLocalHost.port = bound.sin_port; | ||
| 2097 | printf("default port in use by another instance; bound to ephemeral %s instead\n", addrLocalHost.ToString().c_str()); | ||
| 2098 | } | ||
| 2090 | else | 2099 | else |
| 2100 | { | ||
| 2091 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); | 2101 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); |
| 2092 | printf("%s\n", strError.c_str()); | 2102 | printf("%s\n", strError.c_str()); |
| 2093 | return false; | 2103 | return false; |
| 2104 | } | ||
| 2094 | } | 2105 | } |
| 2095 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); | 2106 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); |
| 2096 | 2107 | ||