diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 226 |
1 files changed, 117 insertions, 109 deletions
diff --git a/src/net.cpp b/src/net.cpp index c24547e..bb39122 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -464,28 +464,9 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 464 | 464 | ||
| 465 | 465 | ||
| 466 | 466 | ||
| 467 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | 467 | // AbandonRequests() lived here, sweeping CNode::mapRequests for a callback a |
| 468 | { | 468 | // closing dialog had registered. Nothing ever registered one and nothing ever |
| 469 | // If the dialog might get closed before the reply comes back, | 469 | // called this, so it went with CRequestTracker. |
| 470 | // call this in the destructor so it doesn't get called after it's deleted. | ||
| 471 | CRITICAL_BLOCK(cs_vNodes) | ||
| 472 | { | ||
| 473 | foreach(CNode* pnode, vNodes) | ||
| 474 | { | ||
| 475 | CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 476 | { | ||
| 477 | for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) | ||
| 478 | { | ||
| 479 | CRequestTracker& tracker = (*mi).second; | ||
| 480 | if (tracker.fn == fn && tracker.param1 == param1) | ||
| 481 | pnode->mapRequests.erase(mi++); | ||
| 482 | else | ||
| 483 | mi++; | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | } | ||
| 488 | } | ||
| 489 | 470 | ||
| 490 | 471 | ||
| 491 | 472 | ||
| @@ -493,25 +474,10 @@ void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) | |||
| 493 | 474 | ||
| 494 | 475 | ||
| 495 | 476 | ||
| 496 | // | 477 | // The broadcast/subscribe channel system (AnySubscribed, CNode::IsSubscribed, |
| 497 | // Subscription methods for the broadcast and subscription system. | 478 | // Subscribe, CancelSubscribe) stood here. Its channels were MSG_TABLE and |
| 498 | // Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. | 479 | // MSG_PRODUCT, there was never an inbound handler for "subscribe", |
| 499 | // | 480 | // "sub-cancel", "publish" or "pub-cancel", and the objects it carried are gone. |
| 500 | // The subscription system uses a meet-in-the-middle strategy. | ||
| 501 | // With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers | ||
| 502 | // subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. | ||
| 503 | // | ||
| 504 | |||
| 505 | bool AnySubscribed(unsigned int nChannel) | ||
| 506 | { | ||
| 507 | if (pnodeLocalHost->IsSubscribed(nChannel)) | ||
| 508 | return true; | ||
| 509 | CRITICAL_BLOCK(cs_vNodes) | ||
| 510 | foreach(CNode* pnode, vNodes) | ||
| 511 | if (pnode->IsSubscribed(nChannel)) | ||
| 512 | return true; | ||
| 513 | return false; | ||
| 514 | } | ||
| 515 | 481 | ||
| 516 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | 482 | void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) |
| 517 | { | 483 | { |
| @@ -524,55 +490,6 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) | |||
| 524 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); | 490 | PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); |
| 525 | } | 491 | } |
| 526 | 492 | ||
| 527 | bool CNode::IsSubscribed(unsigned int nChannel) | ||
| 528 | { | ||
| 529 | if (nChannel >= vfSubscribe.size()) | ||
| 530 | return false; | ||
| 531 | return vfSubscribe[nChannel]; | ||
| 532 | } | ||
| 533 | |||
| 534 | void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) | ||
| 535 | { | ||
| 536 | if (nChannel >= vfSubscribe.size()) | ||
| 537 | return; | ||
| 538 | |||
| 539 | if (!AnySubscribed(nChannel)) | ||
| 540 | { | ||
| 541 | // Relay subscribe | ||
| 542 | CRITICAL_BLOCK(cs_vNodes) | ||
| 543 | foreach(CNode* pnode, vNodes) | ||
| 544 | if (pnode != this) | ||
| 545 | pnode->PushMessage("subscribe", nChannel, nHops); | ||
| 546 | } | ||
| 547 | |||
| 548 | vfSubscribe[nChannel] = true; | ||
| 549 | } | ||
| 550 | |||
| 551 | void CNode::CancelSubscribe(unsigned int nChannel) | ||
| 552 | { | ||
| 553 | if (nChannel >= vfSubscribe.size()) | ||
| 554 | return; | ||
| 555 | |||
| 556 | // Prevent from relaying cancel if wasn't subscribed | ||
| 557 | if (!vfSubscribe[nChannel]) | ||
| 558 | return; | ||
| 559 | vfSubscribe[nChannel] = false; | ||
| 560 | |||
| 561 | if (!AnySubscribed(nChannel)) | ||
| 562 | { | ||
| 563 | // Relay subscription cancel | ||
| 564 | CRITICAL_BLOCK(cs_vNodes) | ||
| 565 | foreach(CNode* pnode, vNodes) | ||
| 566 | if (pnode != this) | ||
| 567 | pnode->PushMessage("sub-cancel", nChannel); | ||
| 568 | |||
| 569 | // Clear memory, no longer subscribed | ||
| 570 | if (nChannel == MSG_PRODUCT) | ||
| 571 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 572 | mapProducts.clear(); | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | 493 | ||
| 577 | 494 | ||
| 578 | 495 | ||
| @@ -1074,6 +991,18 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting | |||
| 1074 | void ThreadBtfAccept(void* parg) | 991 | void ThreadBtfAccept(void* parg) |
| 1075 | { | 992 | { |
| 1076 | printf("ThreadBtfAccept started\n"); | 993 | printf("ThreadBtfAccept started\n"); |
| 994 | // In operator mode the pool server registers this same node identity as a | ||
| 995 | // stratum service at the rendezvous relays, and a relay only holds ONE | ||
| 996 | // service registration per pubkey -- peer-accept and pool-accept would | ||
| 997 | // kick each other off forever, breaking both. The pool's registration is | ||
| 998 | // the one that must be stable (that's what miners resolve and dial), so | ||
| 999 | // the node skips inbound rendezvous here and relies on outbound .btf | ||
| 1000 | // connections for block sync. | ||
| 1001 | if (nMineMode == MINE_OPERATOR) | ||
| 1002 | { | ||
| 1003 | printf("ThreadBtfAccept: operator mode -- skipping peer rendezvous registration\n"); | ||
| 1004 | return; | ||
| 1005 | } | ||
| 1077 | unsigned char pk[32]; | 1006 | unsigned char pk[32]; |
| 1078 | unsigned char sk[32]; | 1007 | unsigned char sk[32]; |
| 1079 | while (!BtfGetIdentity(pk, sk)) | 1008 | while (!BtfGetIdentity(pk, sk)) |
| @@ -1223,17 +1152,6 @@ void CNode::Disconnect() | |||
| 1223 | BtfCloseSocket(hSocket); | 1152 | BtfCloseSocket(hSocket); |
| 1224 | hSocket = INVALID_SOCKET; | 1153 | hSocket = INVALID_SOCKET; |
| 1225 | 1154 | ||
| 1226 | // All of a nodes broadcasts and subscriptions are automatically torn down | ||
| 1227 | // when it goes down, so a node has to stay up to keep its broadcast going. | ||
| 1228 | |||
| 1229 | CRITICAL_BLOCK(cs_mapProducts) | ||
| 1230 | for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();) | ||
| 1231 | AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second); | ||
| 1232 | |||
| 1233 | // Cancel subscriptions | ||
| 1234 | for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) | ||
| 1235 | if (vfSubscribe[nChannel]) | ||
| 1236 | CancelSubscribe(nChannel); | ||
| 1237 | } | 1155 | } |
| 1238 | 1156 | ||
| 1239 | 1157 | ||
| @@ -1358,7 +1276,6 @@ void ThreadSocketHandler2(void* parg) | |||
| 1358 | bool fDelete = false; | 1276 | bool fDelete = false; |
| 1359 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1277 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1360 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | 1278 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) |
| 1361 | TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) | ||
| 1362 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) | 1279 | TRY_CRITICAL_BLOCK(pnode->cs_inventory) |
| 1363 | fDelete = true; | 1280 | fDelete = true; |
| 1364 | if (fDelete) | 1281 | if (fDelete) |
| @@ -1466,6 +1383,27 @@ void ThreadSocketHandler2(void* parg) | |||
| 1466 | printf("dropping node %s: its socket is no longer valid\n", | 1383 | printf("dropping node %s: its socket is no longer valid\n", |
| 1467 | pnode->addr.ToString().c_str()); | 1384 | pnode->addr.ToString().c_str()); |
| 1468 | pnode->fDisconnect = true; | 1385 | pnode->fDisconnect = true; |
| 1386 | |||
| 1387 | // Stop referring to it, or it goes into the next poll | ||
| 1388 | // set and fails it again. | ||
| 1389 | // | ||
| 1390 | // Marking fDisconnect is not enough: a node is only | ||
| 1391 | // removed from vNodes once it has drained or once | ||
| 1392 | // DISCONNECT_DRAIN_SECS has passed, so one dead | ||
| 1393 | // descriptor kept failing the wait for a full minute -- | ||
| 1394 | // and a failed wait skips the service loop below | ||
| 1395 | // entirely, which means NO peer is read on that pass. | ||
| 1396 | // Measured on a wedged pool under the old select() | ||
| 1397 | // implementation: 1,027 consecutive passes over 61 | ||
| 1398 | // seconds, one bad socket, the entire node deaf. poll() | ||
| 1399 | // reports a bad descriptor as POLLNVAL on that entry | ||
| 1400 | // rather than failing the whole call, so this path is | ||
| 1401 | // now the backstop rather than the common case. | ||
| 1402 | // | ||
| 1403 | // Not closed, only forgotten. getsockopt just told us | ||
| 1404 | // this descriptor is not ours any more; closing it now | ||
| 1405 | // is how an unrelated socket gets closed instead. | ||
| 1406 | pnode->hSocket = INVALID_SOCKET; | ||
| 1469 | } | 1407 | } |
| 1470 | } | 1408 | } |
| 1471 | } | 1409 | } |
| @@ -1710,6 +1648,13 @@ void ThreadMessageHandler(void* parg) | |||
| 1710 | } | 1648 | } |
| 1711 | } | 1649 | } |
| 1712 | 1650 | ||
| 1651 | // When any peer last told us a height. A wedged node -- one that still has | ||
| 1652 | // peers but has stopped hearing them -- is exactly a node where this stops | ||
| 1653 | // advancing, and that is the one thing the height comparison itself cannot | ||
| 1654 | // see: there is no height to compare. | ||
| 1655 | static int64 nLastPeerHeightSeen = 0; | ||
| 1656 | static int64 nPeerHeightFirstAsked = 0; | ||
| 1657 | |||
| 1713 | int GetPeerMedianHeight() | 1658 | int GetPeerMedianHeight() |
| 1714 | { | 1659 | { |
| 1715 | vector<int> vHeights; | 1660 | vector<int> vHeights; |
| @@ -1721,10 +1666,26 @@ int GetPeerMedianHeight() | |||
| 1721 | if (vHeights.empty()) | 1666 | if (vHeights.empty()) |
| 1722 | return -1; | 1667 | return -1; |
| 1723 | 1668 | ||
| 1669 | nLastPeerHeightSeen = GetTime(); | ||
| 1724 | sort(vHeights.begin(), vHeights.end()); | 1670 | sort(vHeights.begin(), vHeights.end()); |
| 1725 | return vHeights[vHeights.size() / 2]; | 1671 | return vHeights[vHeights.size() / 2]; |
| 1726 | } | 1672 | } |
| 1727 | 1673 | ||
| 1674 | // Seconds since any peer last reported a height. | ||
| 1675 | // | ||
| 1676 | // Before ever hearing one, measure from the first time somebody asked rather | ||
| 1677 | // than from the epoch -- otherwise a node refuses jobs the instant it starts, | ||
| 1678 | // before it has had a chance to complete a single handshake. | ||
| 1679 | int64 GetPeerHeightAgeSecs() | ||
| 1680 | { | ||
| 1681 | int64 nNow = GetTime(); | ||
| 1682 | if (nPeerHeightFirstAsked == 0) | ||
| 1683 | nPeerHeightFirstAsked = nNow; | ||
| 1684 | if (nLastPeerHeightSeen == 0) | ||
| 1685 | return nNow - nPeerHeightFirstAsked; | ||
| 1686 | return nNow - nLastPeerHeightSeen; | ||
| 1687 | } | ||
| 1688 | |||
| 1728 | 1689 | ||
| 1729 | // Say out loud when we are behind the network. | 1690 | // Say out loud when we are behind the network. |
| 1730 | // | 1691 | // |
| @@ -1861,7 +1822,21 @@ void ThreadBitcoinMiner(void* parg) | |||
| 1861 | // Start the miners. Returns how many actually got off the ground. | 1822 | // Start the miners. Returns how many actually got off the ground. |
| 1862 | int StartMinerThreads() | 1823 | int StartMinerThreads() |
| 1863 | { | 1824 | { |
| 1864 | int nThreads = MinerThreadCount(); | 1825 | // MinerThreadCount() sizes the SOLO miner, where each thread hashes a |
| 1826 | // template of its own and more threads is simply more hashrate. | ||
| 1827 | // | ||
| 1828 | // Participant mode is not that shape. Every thread that enters | ||
| 1829 | // BitcoinMiner() takes the MINE_PARTICIPANT branch, and that branch is a | ||
| 1830 | // whole miner: its own Stratum session to the pool, its own payout address | ||
| 1831 | // from the rotation, and its own pool of /hashthreads worker threads. So | ||
| 1832 | // the two counts multiply instead of dividing, and the machine ends up | ||
| 1833 | // running MinerThreadCount() x nHashThreads hashing threads plus one pool | ||
| 1834 | // connection each. On a 12-core host with /hashthreads=2 that is 11 | ||
| 1835 | // sessions and 22 hash threads -- roughly 2x the cores, which is not more | ||
| 1836 | // work done, only more contention -- and with /hashthreads unset it is 11 | ||
| 1837 | // sessions of 11 workers. One session is the entirety of participant mode; | ||
| 1838 | // /hashthreads is what sizes it, and it can then mean what it says. | ||
| 1839 | int nThreads = (nMineMode == MINE_PARTICIPANT) ? 1 : MinerThreadCount(); | ||
| 1865 | int nStarted = 0; | 1840 | int nStarted = 0; |
| 1866 | for (int i = 0; i < nThreads; i++) | 1841 | for (int i = 0; i < nThreads; i++) |
| 1867 | { | 1842 | { |
| @@ -1873,9 +1848,24 @@ int StartMinerThreads() | |||
| 1873 | // The dataset is one allocation shared by all of them; only the 2 MB | 1848 | // The dataset is one allocation shared by all of them; only the 2 MB |
| 1874 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | 1849 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first |
| 1875 | // question anyone asks. | 1850 | // question anyone asks. |
| 1876 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | 1851 | // |
| 1877 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | 1852 | // Participant mode reports the workers that session will run, not the one |
| 1878 | nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); | 1853 | // thread that runs it: the hash thread count is the number an operator |
| 1854 | // watches, and it is the number that was wrong here. It is otherwise only | ||
| 1855 | // logged under -debug, which is how 11 sessions ran unnoticed. | ||
| 1856 | if (nMineMode == MINE_PARTICIPANT) | ||
| 1857 | { | ||
| 1858 | int nWorkers = ParticipantHashThreadCount(); | ||
| 1859 | printf("Mining for the pool with %d hash thread(s) of %u core(s) -- about %d MB " | ||
| 1860 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1861 | nWorkers, AvailableCoreCount(), 2080 + 2 * nWorkers); | ||
| 1862 | } | ||
| 1863 | else | ||
| 1864 | { | ||
| 1865 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 1866 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1867 | nStarted, AvailableCoreCount(), 2080 + 2 * nStarted); | ||
| 1868 | } | ||
| 1879 | return nStarted; | 1869 | return nStarted; |
| 1880 | } | 1870 | } |
| 1881 | 1871 | ||
| @@ -1950,11 +1940,29 @@ bool StartNode(string& strError) | |||
| 1950 | { | 1940 | { |
| 1951 | int nErr = WSAGetLastError(); | 1941 | int nErr = WSAGetLastError(); |
| 1952 | if (nErr == WSAEADDRINUSE) | 1942 | if (nErr == WSAEADDRINUSE) |
| 1953 | strError = strprintf("Error: Unable to bind to port %s on this computer. The program is probably already running.", addrLocalHost.ToString().c_str()); | 1943 | { |
| 1944 | // Another local instance holds the default port. Fall back to an | ||
| 1945 | // ephemeral port: inbound direct-IP peering is optional, and .btf | ||
| 1946 | // rendezvous connectivity is unaffected by the local listen port. | ||
| 1947 | sockaddr.sin_port = htons(0); | ||
| 1948 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | ||
| 1949 | { | ||
| 1950 | strError = strprintf("Error: Unable to bind any port on this computer (bind returned error %d)", WSAGetLastError()); | ||
| 1951 | printf("%s\n", strError.c_str()); | ||
| 1952 | return false; | ||
| 1953 | } | ||
| 1954 | struct sockaddr_in bound; | ||
| 1955 | socklen_t nBoundLen = sizeof(bound); | ||
| 1956 | if (getsockname(hListenSocket, (struct sockaddr*)&bound, &nBoundLen) == 0) | ||
| 1957 | addrLocalHost.port = bound.sin_port; | ||
| 1958 | printf("default port in use by another instance; bound to ephemeral %s instead\n", addrLocalHost.ToString().c_str()); | ||
| 1959 | } | ||
| 1954 | else | 1960 | else |
| 1961 | { | ||
| 1955 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); | 1962 | strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); |
| 1956 | printf("%s\n", strError.c_str()); | 1963 | printf("%s\n", strError.c_str()); |
| 1957 | return false; | 1964 | return false; |
| 1965 | } | ||
| 1958 | } | 1966 | } |
| 1959 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); | 1967 | printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); |
| 1960 | 1968 | ||