<NME> net.cpp <BEF> // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "validation.h" #include "net.h" #include "addrman.h" #include "chainparams.h" #include "clientversion.h" #include "consensus/consensus.h" #include "crypto/common.h" #include "crypto/sha256.h" #include "hash.h" #include "primitives/transaction.h" #include "netbase.h" #include "scheduler.h" #include "ui_interface.h" #include "utilstrencodings.h" #include "consensus/validation.h" #include "txmempool.h" #include "./consensus/validation.h" #include "masternode-sync.h" #include "llmq/quorums_instantsend.h" #include "evo/mnauth.h" #ifdef WIN32 #include <string.h> #else #include <fcntl.h> #endif #ifdef USE_UPNP #include <miniupnpc/miniupnpc.h> #include <miniupnpc/miniwget.h> #include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnperrors.h> #endif #include <math.h> // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 // We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. #define FEELER_SLEEP_WINDOW 1 #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif // Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h. // Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version. #ifdef WIN32 #ifndef PROTECTION_LEVEL_UNRESTRICTED #define PROTECTION_LEVEL_UNRESTRICTED 10 #endif #ifndef IPV6_PROTECTION_LEVEL #define IPV6_PROTECTION_LEVEL 23 #endif #endif namespace { const int MAX_OUTBOUND_CONNECTIONS = 8; const int MAX_FEELER_CONNECTIONS = 1; struct ListenSocket { SOCKET socket; bool whitelisted; ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} }; } constexpr const CConnman::CFullyConnectedOnly CConnman::FullyConnectedOnly; constexpr const CConnman::CAllNodes CConnman::AllNodes; const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8] static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8] // Public Dandelion fields. // All transactions embargoed by dandelion. std::map<uint256, int64_t> CNode::mDandelionEmbargo; // Inbound connections. Transactions from each connection // are broadcast to one of 2 dandelion destinations. std::vector<CNode*> CNode::vDandelionInbound; // All of our outbound destinations. CCriticalSection cs_nLastNodeId; static CSemaphore *semOutbound = NULL; // Signals for message handling static CNodeSignals g_signals; CNode* CNode::localDandelionDestination = nullptr; // All txn are put in the stempool in stem phase. // After getting relayed they are moved to mempool. extern CTxPoolAggregate txpools; /** Services this node implementation cares about */ ServiceFlags nRelevantServices = NODE_NETWORK; // // Global state variables // bool fDiscover = true; bool fListen = true; bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; std::map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfLimited[NET_MAX] = {}; std::string strSubVersion; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); // Signals for message handling static CNodeSignals g_signals; CNodeSignals& GetNodeSignals() { return g_signals; } void CConnman::AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); } unsigned short GetListenPort() { return (unsigned short)(GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer bool GetLocal(CService& addr, const CNetAddr *paddrPeer) { if (!fListen) return false; int nBestScore = -1; int nBestReachability = -1; { LOCK(cs_mapLocalHost); for (std::map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { int nScore = (*it).second.nScore; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService((*it).first, (*it).second.nPort); nBestReachability = nReachability; nBestScore = nScore; } } } return nBestScore >= 0; } //! Convert the pnSeeds6 array into usable address objects. static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, // it'll get a pile of addresses with newer timestamps. // Seed nodes are given a random 'last seen time' of between one and two // weeks ago. const int64_t nOneWeek = 7*24*60*60; std::vector<CAddress> vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) { struct in6_addr ip; memcpy(&ip, i->addr, sizeof(ip)); CAddress addr(CService(ip, i->port), NODE_NETWORK); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } return vSeedsOut; } // get best local address for a particular peer as a CAddress // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) { CAddress ret(CService(CNetAddr(),GetListenPort()), nLocalServices); CService addr; if (GetLocal(addr, paddrPeer)) { ret = CAddress(addr, nLocalServices); } ret.nTime = GetAdjustedTime(); return ret; } int GetnScore(const CService& addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == LOCAL_NONE) return 0; return mapLocalHost[addr].nScore; } // Is our peer's addrLocal potentially useful as an external IP source? bool IsPeerAddrLocalGood(CNode *pnode) { CService addrLocal = pnode->GetAddrLocal(); return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() && !IsLimited(addrLocal.GetNetwork()); } // pushes our own address to a peer void AdvertiseLocal(CNode *pnode) { if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0)) { addrLocal.SetIP(pnode->GetAddrLocal()); } if (addrLocal.IsRoutable()) { LogPrint("net", "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); FastRandomContext insecure_rand; pnode->PushAddress(addrLocal, insecure_rand); } } } // learn a new local address bool AddLocal(const CService& addr, int nScore) { if (!addr.IsRoutable()) return false; if (!fDiscover && nScore < LOCAL_MANUAL) return false; if (IsLimited(addr)) return false; LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore); { LOCK(cs_mapLocalHost); bool fAlready = mapLocalHost.count(addr) > 0; LocalServiceInfo &info = mapLocalHost[addr]; if (!fAlready || nScore >= info.nScore) { info.nScore = nScore + (fAlready ? 1 : 0); info.nPort = addr.GetPort(); } } return true; } bool AddLocal(const CNetAddr &addr, int nScore) { return AddLocal(CService(addr, GetListenPort()), nScore); } bool RemoveLocal(const CService& addr) { LOCK(cs_mapLocalHost); LogPrintf("RemoveLocal(%s)\n", addr.ToString()); mapLocalHost.erase(addr); return true; } /** Make a particular network entirely off-limits (no automatic connects to it) */ void SetLimited(enum Network net, bool fLimited) { if (net == NET_UNROUTABLE) return; LOCK(cs_mapLocalHost); vfLimited[net] = fLimited; } bool IsLimited(enum Network net) { LOCK(cs_mapLocalHost); return vfLimited[net]; } bool IsLimited(const CNetAddr &addr) { return IsLimited(addr.GetNetwork()); } /** vote for a local address */ bool SeenLocal(const CService& addr) { { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) return false; mapLocalHost[addr].nScore++; } return true; } /** check whether a given address is potentially local */ bool IsLocal(const CService& addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } /** check whether a given network is one we can probably connect to */ bool IsReachable(enum Network net) { LOCK(cs_mapLocalHost); return !vfLimited[net]; } /** check whether a given address is in a network we can probably connect to */ bool IsReachable(const CNetAddr& addr) { enum Network net = addr.GetNetwork(); return IsReachable(net); } CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if ((CNetAddr)pnode->addr == ip) return (pnode); return NULL; } CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (subNet.Match((CNetAddr)pnode->addr)) return (pnode); return NULL; } CNode* CConnman::FindNode(const std::string& addrName) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->GetAddrName() == addrName) { return (pnode); } } return NULL; } CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if ((CService)pnode->addr == addr) return (pnode); return NULL; } bool CConnman::CheckIncomingNonce(uint64_t nonce) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce) return false; } return true; } CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool fAllowLocal) { if (pszDest == NULL) { // we clean znode connections in CZnodeMan::ProcessZnodeConnections() // so should be safe to skip this and connect to local Hot MN on CActiveZnode::ManageState() if (!fAllowLocal && IsLocal(addrConnect)) return NULL; LOCK(cs_vNodes); // Look for an existing connection CNode* pnode = FindNode((CService)addrConnect); if (pnode) { LogPrintf("Failed to open new connection, already connected\n"); return NULL; } } /// debug print LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); // Connect SOCKET hSocket; bool proxyConnectionFailed = false; if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) { if (!IsSelectableSocket(hSocket)) { LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); CloseSocket(hSocket); return NULL; } if (pszDest && addrConnect.IsValid()) { // It is possible that we already have a connection to the IP/port pszDest resolved to. // In that case, drop the connection that was just created, and return the existing CNode instead. // Also store the name we used to connect in that CNode, so that future FindNode() calls to that // name catch this early. LOCK(cs_vNodes); CNode* pnode = FindNode((CService)addrConnect); if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); CloseSocket(hSocket); LogPrintf("Failed to open new connection, already connected\n"); return NULL; } } addrman.Attempt(addrConnect, fCountFailure); // Add node NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, pszDest ? pszDest : "", false); pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); pnode->AddRef(); return pnode; } else if (!proxyConnectionFailed) { // If connecting to the node failed, and failure is not caused by a problem connecting to // the proxy, mark this as an attempt. addrman.Attempt(addrConnect, fCountFailure); } return NULL; } void CConnman::DumpBanlist() { SweepBanned(); // clean unused entries (if bantime has expired) if (!BannedSetIsDirty()) return; int64_t nStart = GetTimeMillis(); CBanDB bandb; banmap_t banmap; GetBanned(banmap); if (bandb.Write(banmap)) { SetBannedSetDirty(false); } LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } void CNode::CloseSocketDisconnect() { fDisconnect = true; LOCK(cs_hSocket); if (hSocket != INVALID_SOCKET) { LogPrint("net", "disconnecting peer=%d\n", id); CloseSocket(hSocket); } } void CConnman::ClearBanned() { { LOCK(cs_setBanned); setBanned.clear(); setBannedIsDirty = true; } DumpBanlist(); //store banlist to disk if(clientInterface) clientInterface->BannedListChanged(); } bool CConnman::IsBanned(CNetAddr ip) { bool fResult = false; { LOCK(cs_setBanned); for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) { CSubNet subNet = (*it).first; CBanEntry banEntry = (*it).second; if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil) fResult = true; } } return fResult; } bool CConnman::IsBanned(CSubNet subnet) { bool fResult = false; { LOCK(cs_setBanned); banmap_t::iterator i = setBanned.find(subnet); if (i != setBanned.end()) { CBanEntry banEntry = (*i).second; if (GetTime() < banEntry.nBanUntil) fResult = true; } } return fResult; } pch += handled; nBytes -= handled; if (msg.complete()) msg.nTime = GetTimeMicros(); } return true; bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); sinceUnixEpoch = false; } banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; { LOCK(cs_setBanned); if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { setBanned[subNet] = banEntry; setBannedIsDirty = true; } else return; } if(clientInterface) clientInterface->BannedListChanged(); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (subNet.Match((CNetAddr)pnode->addr)) pnode->fDisconnect = true; } } if(banReason == BanReasonManuallyAdded) DumpBanlist(); //store banlist to disk immediately if user requested ban } bool CConnman::Unban(const CNetAddr &addr) { CSubNet subNet(addr); return Unban(subNet); } bool CConnman::Unban(const CSubNet &subNet) { { LOCK(cs_setBanned); if (!setBanned.erase(subNet)) return false; setBannedIsDirty = true; } if(clientInterface) clientInterface->BannedListChanged(); DumpBanlist(); //store banlist to disk immediately return true; } void CConnman::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); // Sweep the banlist so expired bans are not returned SweepBanned(); banMap = setBanned; //create a thread safe copy } void CConnman::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; setBannedIsDirty = true; } void CConnman::SweepBanned() { int64_t now = GetTime(); LOCK(cs_setBanned); banmap_t::iterator it = setBanned.begin(); while(it != setBanned.end()) { CSubNet subNet = (*it).first; CBanEntry banEntry = (*it).second; if(now > banEntry.nBanUntil) { setBanned.erase(it++); setBannedIsDirty = true; LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); } else ++it; } } bool CConnman::BannedSetIsDirty() { LOCK(cs_setBanned); return setBannedIsDirty; } void CConnman::SetBannedSetDirty(bool dirty) { LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag setBannedIsDirty = dirty; } bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { LOCK(cs_vWhitelistedRange); BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { if (subnet.Match(addr)) return true; } return false; } void CConnman::AddWhitelistedRange(const CSubNet &subnet) { LOCK(cs_vWhitelistedRange); vWhitelistedRange.push_back(subnet); } std::string CNode::GetAddrName() const { LOCK(cs_addrName); return addrName; } void CNode::MaybeSetAddrName(const std::string& addrNameIn) { LOCK(cs_addrName); if (addrName.empty()) { addrName = addrNameIn; } } CService CNode::GetAddrLocal() const { LOCK(cs_addrLocal); return addrLocal; } void CNode::SetAddrLocal(const CService& addrLocalIn) { LOCK(cs_addrLocal); if (addrLocal.IsValid()) { error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString()); } else { addrLocal = addrLocalIn; } } #undef X #define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); X(nServices); X(addr); { LOCK(cs_filter); X(fRelayTxes); } X(nLastSend); X(nLastRecv); X(nTimeConnected); X(nTimeOffset); stats.addrName = GetAddrName(); X(nVersion); { LOCK(cs_SubVer); X(cleanSubVer); } X(fInbound); X(fAddnode); X(nStartingHeight); { LOCK(cs_vSend); X(mapSendBytesPerMsgCmd); X(nSendBytes); } { LOCK(cs_vRecv); X(mapRecvBytesPerMsgCmd); X(nRecvBytes); } X(fWhitelisted); // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. // Merely reporting pingtime might fool the caller into thinking the node was still responsive, // since pingtime does not update until the ping is complete, which might take a while. // So, if a ping is taking an unusually long time in flight, // the caller can immediately detect that this is happening. int64_t nPingUsecWait = 0; if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) { nPingUsecWait = GetTimeMicros() - nPingUsecStart; } // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) stats.dPingTime = (((double)nPingUsecTime) / 1e6); stats.dMinPing = (((double)nMinPingUsecTime) / 1e6); stats.dPingWait = (((double)nPingUsecWait) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; { LOCK(cs_mnauth); X(verifiedProRegTxHash); } } #undef X bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) { complete = false; int64_t nTimeMicros = GetTimeMicros(); LOCK(cs_vRecv); nLastRecv = nTimeMicros / 1000000; nRecvBytes += nBytes; while (nBytes > 0) { // get current incomplete message, or create a new one if (vRecvMsg.empty() || vRecvMsg.back().complete()) vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION)); CNetMessage& msg = vRecvMsg.back(); // absorb network data int handled; if (!msg.in_data) { handled = msg.readHeader(pch, nBytes); if (msg.in_data && nTimeFirstMessageReceived == 0) { if (fSuccessfullyConnected) { // First message after VERSION/VERACK. // We record the time when the header is fully received and not when the full message is received. // otherwise a peer might send us a very large message as first message after VERSION/VERACK and fill // up our memory with multiple parallel connections doing this. nTimeFirstMessageReceived = nTimeMicros; fFirstMessageIsMNAUTH = msg.hdr.GetCommand() == NetMsgType::MNAUTH; } else { // We're still in the VERSION/VERACK handshake process, so any message received in this state is // expected to be very small. This protects against attackers filling up memory by sending oversized // VERSION messages while the incoming connection is still protected against eviction if (msg.hdr.nMessageSize > 1024) { LogPrint("net", "Oversized VERSION/VERACK message from peer=%i, disconnecting\n", GetId()); return false; } } } } else handled = msg.readData(pch, nBytes); if (handled < 0) return false; if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { LogPrint("net", "Oversized message from peer=%i, disconnecting\n", GetId()); return false; } pch += handled; nBytes -= handled; if (msg.complete()) { //store received bytes per message command //to prevent a memory DOS, only allow valid commands mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.hdr.pchCommand); if (i == mapRecvBytesPerMsgCmd.end()) i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER); assert(i != mapRecvBytesPerMsgCmd.end()); i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; msg.nTime = nTimeMicros; complete = true; } } return true; } void CNode::SetSendVersion(int nVersionIn) { // Send version may only be changed in the version message, and // only one version message is allowed per session. We can therefore // treat this value as const and even atomic as long as it's only used // once a version message has been successfully processed. Any attempt to // set this twice is an error. if (nSendVersion != 0) { error("Send version already set for node: %i. Refusing to change from %i to %i", id, nSendVersion, nVersionIn); } else { nSendVersion = nVersionIn; } } int CNode::GetSendVersion() const { // The send version should always be explicitly set to // INIT_PROTO_VERSION rather than using this value until SetSendVersion // has been called. if (nSendVersion == 0) { error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION); return INIT_PROTO_VERSION; } return nSendVersion; } int CNetMessage::readHeader(const char *pch, unsigned int nBytes) { // copy data to temporary parsing buffer unsigned int nRemaining = 24 - nHdrPos; unsigned int nCopy = std::min(nRemaining, nBytes); memcpy(&hdrbuf[nHdrPos], pch, nCopy); nHdrPos += nCopy; // if header incomplete, exit if (nHdrPos < 24) return nCopy; // deserialize to CMessageHeader try { hdrbuf >> hdr; } catch (const std::exception&) { return -1; } // reject messages larger than MAX_SIZE if (hdr.nMessageSize > MAX_SIZE) return -1; // switch state to reading message data in_data = true; return nCopy; } int CNetMessage::readData(const char *pch, unsigned int nBytes) { unsigned int nRemaining = hdr.nMessageSize - nDataPos; unsigned int nCopy = std::min(nRemaining, nBytes); if (vRecv.size() < nDataPos + nCopy) { // Allocate up to 256 KiB ahead, but never more than the total message size. vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); } hasher.Write((const unsigned char*)pch, nCopy); memcpy(&vRecv[nDataPos], pch, nCopy); nDataPos += nCopy; return nCopy; } const uint256& CNetMessage::GetMessageHash() const { assert(complete()); if (data_hash.IsNull()) hasher.Finalize(data_hash.begin()); return data_hash; } // requires LOCK(cs_vSend) size_t CConnman::SocketSendData(CNode *pnode) const { auto it = pnode->vSendMsg.begin(); size_t nSentSize = 0; while (it != pnode->vSendMsg.end()) { const auto &data = *it; assert(data.size() > pnode->nSendOffset); int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) break; nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes > 0) { pnode->nLastSend = GetSystemTimeInSeconds(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; nSentSize += nBytes; if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; it++; } else { // could not send full message; stop sending more break; } } else { if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { LogPrintf("socket send error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } } // couldn't send anything at all break; } } if (it == pnode->vSendMsg.end()) { assert(pnode->nSendOffset == 0); assert(pnode->nSendSize == 0); } pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); return nSentSize; } struct NodeEvictionCandidate { NodeId id; int64_t nTimeConnected; int64_t nMinPingUsecTime; int64_t nLastBlockTime; int64_t nLastTXTime; bool fRelevantServices; bool fRelayTxes; bool fBloomFilter; CAddress addr; uint64_t nKeyedNetGroup; }; static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nMinPingUsecTime > b.nMinPingUsecTime; } static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nTimeConnected > b.nTimeConnected; } static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nKeyedNetGroup < b.nKeyedNetGroup; } static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block. if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime; if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices; return a.nTimeConnected > b.nTimeConnected; } static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn. if (a.nLastTXTime != b.nLastTXTime) return a.nLastTXTime < b.nLastTXTime; if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes; if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter; return a.nTimeConnected > b.nTimeConnected; } /** Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker * triggered network partitioning. * The strategy used here is to protect a small number of peers * for each of several distinct characteristics which are difficult * to forge. In order to partition a node the attacker must be * simultaneously better at all of them than honest peers. */ bool CConnman::AttemptToEvictConnection() { std::vector<NodeEvictionCandidate> vEvictionCandidates; { LOCK(cs_vNodes); BOOST_FOREACH(CNode *node, vNodes) { if (node->fWhitelisted) continue; if (!node->fInbound) continue; if (node->fDisconnect) continue; if (fMasternodeMode) { // This handles eviction protected nodes. Nodes are always protected for a short time after the connection // was accepted. This short time is meant for the VERSION/VERACK exchange and the possible MNAUTH that might // follow when the incoming connection is from another masternode. When another message then MNAUTH // is received after VERSION/VERACK, the protection is lifted immediately. bool isProtected = GetSystemTimeInSeconds() - node->nTimeConnected < INBOUND_EVICTION_PROTECTION_TIME; if (node->nTimeFirstMessageReceived != 0 && !node->fFirstMessageIsMNAUTH) { isProtected = false; } // if MNAUTH was valid, the node is always protected (and at the same time not accounted when // checking incoming connection limits) if (!node->verifiedProRegTxHash.IsNull()) { isProtected = true; } if (isProtected) { continue; } } NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, (node->nServices & nRelevantServices) == nRelevantServices, node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } } if (vEvictionCandidates.empty()) return false; // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes closer to the target. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect 4 nodes that most recently sent us transactions. // An attacker cannot manipulate this metric without performing useful work. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect 4 nodes that most recently sent us blocks. // An attacker cannot manipulate this metric without performing useful work. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect the half of the remaining nodes which have been connected the longest. // This replicates the non-eviction implicit behavior, and precludes attacks that start later. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes; BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; nMostConnectionsTime = grouptime; naMostConnections = node.nKeyedNetGroup; } } // Reduce to the network group with the most connections vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { if ((*it)->GetId() == evicted) { (*it)->fDisconnect = true; return true; } } return false; } // Randomly selects the destination CNode from a list of destination nodes // that have minimal number of routs from "mDandelionRoutes" going to them. CNode* CNode::SelectFromDandelionDestinations() { std::map<CNode*,uint64_t> mDandelionDestinationCounts; for (size_t i = 0; i < vDandelionDestination.size(); i++) { mDandelionDestinationCounts.insert(std::make_pair(vDandelionDestination.at(i),0)); } for (auto const& f : mDandelionRoutes) { if (mDandelionDestinationCounts.find(f.second) != mDandelionDestinationCounts.end()) mDandelionDestinationCounts[f.second]++; } unsigned int minNumConnections = vDandelionInbound.size(); for (auto const& e : mDandelionDestinationCounts) { if (e.second < minNumConnections) { minNumConnections = e.second; } } std::vector<CNode*> candidateDestinations; for (auto const& e : mDandelionDestinationCounts) { if (e.second == minNumConnections) { candidateDestinations.push_back(e.first); } } FastRandomContext rng; CNode* dandelionDestination = nullptr; if (candidateDestinations.size() > 0) { dandelionDestination = candidateDestinations.at(rng.rand32() % candidateDestinations.size()); } return dandelionDestination; } std::string CNode::GetDandelionRoutingDataDebugString() { std::string dandelionRoutingDataDebugString = ""; dandelionRoutingDataDebugString.append(" vDandelionInbound: "); for(auto const& e : vDandelionInbound) { dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" "); } dandelionRoutingDataDebugString.append("\n"); dandelionRoutingDataDebugString.append(" vDandelionOutbound: "); for(auto const& e : vDandelionOutbound) { dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" "); } dandelionRoutingDataDebugString.append("\n"); dandelionRoutingDataDebugString.append(" vDandelionDestination: "); for(auto const& e : vDandelionDestination) { dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" "); } dandelionRoutingDataDebugString.append("\n"); dandelionRoutingDataDebugString.append(" mDandelionRoutes: "); for(auto const& e : mDandelionRoutes) { dandelionRoutingDataDebugString.append("("+std::to_string(e.first->GetId())+","+std::to_string(e.second->GetId())+") "); } dandelionRoutingDataDebugString.append("\n"); dandelionRoutingDataDebugString.append(" localDandelionDestination: "); if(localDandelionDestination == nullptr) { dandelionRoutingDataDebugString.append("nullptr"); } else { dandelionRoutingDataDebugString.append(std::to_string(localDandelionDestination->GetId())); } dandelionRoutingDataDebugString.append("\n"); return dandelionRoutingDataDebugString; } void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr *) &sockaddr, &len); CAddress addr; int nInbound = 0; int nVerifiedInboundMasternodes = 0; int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler); if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) LogPrintf("Warning: Unknown socket family\n"); bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->fInbound) { nInbound++; if (!pnode->verifiedProRegTxHash.IsNull()) { nVerifiedInboundMasternodes++; } } } } if (hSocket == INVALID_SOCKET) { int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK) LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); return; } if (!fNetworkActive) { LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString()); CloseSocket(hSocket); return; } if (!IsSelectableSocket(hSocket)) { LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); CloseSocket(hSocket); return; } // According to the internet TCP_NODELAY is not carried into accepted sockets // on all platforms. Set it again here just to be sure. int set = 1; #ifdef WIN32 setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int)); #else setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); #endif if (IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); return; } if (nInbound - nVerifiedInboundMasternodes >= nMaxInbound) { if (!AttemptToEvictConnection()) { // No connection to evict, disconnect the new connection LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); CloseSocket(hSocket); return; } } NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; GetNodeSignals().InitializeNode(pnode, *this); LogPrint("net", "connection from %s accepted\n", addr.ToString()); { LOCK(cs_vNodes); vNodes.push_back(pnode); // Dandelion: new inbound connection CNode::vDandelionInbound.push_back(pnode); CNode* pto = CNode::SelectFromDandelionDestinations(); if (pto != nullptr) { CNode::mDandelionRoutes.insert(std::make_pair(pnode, pto)); } } } void CNode::CloseDandelionConnections(const CNode* const pnode) { // Remove pnode from vDandelionInbound, if present for (auto iter=vDandelionInbound.begin(); iter!=vDandelionInbound.end();) { if (*iter==pnode) { iter=vDandelionInbound.erase(iter); } else { iter++; } } // Remove pnode from vDandelionOutbound, if present for (auto iter=vDandelionOutbound.begin(); iter!=vDandelionOutbound.end();) { if (*iter==pnode) { iter=vDandelionOutbound.erase(iter); } else { iter++; } } // Remove pnode from vDandelionDestination, if present bool isDandelionDestination = false; for (auto iter = vDandelionDestination.begin(); iter != vDandelionDestination.end();) { if (*iter == pnode) { isDandelionDestination = true; iter=vDandelionDestination.erase(iter); } else { iter++; } } // Generate a replacement Dandelion destination, if necessary if (isDandelionDestination) { // Gather a vector of candidate replacements (outbound peers that are not already destinations) std::vector<CNode*> candidateReplacements; for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) { bool eligibleCandidate = true; for (auto iterj=vDandelionDestination.begin(); iterj!=vDandelionDestination.end();) { if (*iteri==*iterj) { eligibleCandidate = false; iterj = vDandelionDestination.end(); } else { iterj++; } } if (eligibleCandidate) { candidateReplacements.push_back(*iteri); } iteri++; } // Select a candidate to be the replacement destination FastRandomContext rng; CNode* replacementDestination = nullptr; if (candidateReplacements.size() > 0) { replacementDestination = candidateReplacements.at(rng.rand32() % candidateReplacements.size()); } if (replacementDestination != nullptr) { vDandelionDestination.push_back(replacementDestination); } } // Generate a replacement pnode, to be used if necessary CNode* newPto = CNode::SelectFromDandelionDestinations(); // Remove from mDandelionRoutes, if present; if destination, try to replace for(auto iter=mDandelionRoutes.begin(); iter!=mDandelionRoutes.end();) { if (iter->first==pnode) { iter = mDandelionRoutes.erase(iter); } else if (iter->second==pnode) { if (newPto==nullptr) { iter = mDandelionRoutes.erase(iter); } else { iter->second = newPto; iter++; } } else { iter++; } } // Replace localDandelionDestination if equal to pnode if (localDandelionDestination == pnode) { localDandelionDestination = newPto; } } void ThreadMessageHandler() { SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); while (true) { { LOCK(cs_vNodes); // Disconnect unused nodes std::vector<CNode*> vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); // release outbound grant (if any) pnode->grantOutbound.Release(); pnode->grantMasternodeOutbound.Release(); // close socket and cleanup pnode->CloseSocketDisconnect(); // hold in disconnected pool until all refs are released pnode->Release(); vNodesDisconnected.push_back(pnode); } } } { // Delete disconnected nodes std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) { // wait until threads are done using it if (pnode->GetRefCount() <= 0) { bool fDelete = false; { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { fDelete = true; } } } if (fDelete) { // Dandelion: close connection CNode::CloseDandelionConnections(pnode); vNodesDisconnected.remove(pnode); DeleteNode(pnode); } } } } size_t vNodesSize; { LOCK(cs_vNodes); } if (fSleep) MilliSleep(100); } } // // Find which sockets have data to receive // struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 50000; // frequency to poll pnode->vSend fd_set fdsetRecv; fd_set fdsetSend; fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; bool have_fds = false; BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { FD_SET(hListenSocket.socket, &fdsetRecv); hSocketMax = std::max(hSocketMax, hListenSocket.socket); have_fds = true; } { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { // Implement the following logic: // * If there is data to send, select() for sending data. As this only // happens when optimistic write failed, we choose to first drain the // write buffer in this case before receiving more. This avoids // needlessly queueing received data, if the remote peer is not themselves // receiving data. This means properly utilizing TCP flow control signalling. // * Otherwise, if there is space left in the receive buffer, select() for // receiving data. // * Hand off all complete messages to the processor, to be handled without // blocking here. bool select_recv = !pnode->fPauseRecv; bool select_send; { LOCK(pnode->cs_vSend); select_send = !pnode->vSendMsg.empty(); } LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) continue; FD_SET(pnode->hSocket, &fdsetError); hSocketMax = std::max(hSocketMax, pnode->hSocket); have_fds = true; if (select_send) { FD_SET(pnode->hSocket, &fdsetSend); continue; } if (select_recv) { FD_SET(pnode->hSocket, &fdsetRecv); } } } int nSelect = select(have_fds ? hSocketMax + 1 : 0, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); if (interruptNet) return; if (nSelect == SOCKET_ERROR) { if (have_fds) { int nErr = WSAGetLastError(); LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); for (unsigned int i = 0; i <= hSocketMax; i++) FD_SET(i, &fdsetRecv); } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000))) return; } // // Accept new connections // BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { AcceptConnection(hListenSocket); } } // // Service each socket // std::vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->AddRef(); } BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (interruptNet) return; // // Receive // bool recvSet = false; bool sendSet = false; bool errorSet = false; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) continue; recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); errorSet = FD_ISSET(pnode->hSocket, &fdsetError); } if (recvSet || errorSet) { { { // typical socket buffer is 8K-64K char pchBuf[0x10000]; int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) continue; nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } if (nBytes > 0) { bool notify = false; if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); RecordBytesRecv(nBytes); if (notify) { size_t nSizeAdded = 0; auto it(pnode->vRecvMsg.begin()); for (; it != pnode->vRecvMsg.end(); ++it) { if (!it->complete()) break; nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; } { LOCK(pnode->cs_vProcessMsg); pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); pnode->nProcessQueueSize += nSizeAdded; pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; } WakeMessageHandler(); } } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) LogPrint("net", "socket closed\n"); pnode->CloseSocketDisconnect(); } else if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } } } } } // // Send // if (sendSet) { LOCK(pnode->cs_vSend); size_t nBytes = SocketSendData(pnode); if (nBytes) { RecordBytesSent(nBytes); } } // // Inactivity checking // int64_t nTime = GetSystemTimeInSeconds(); if (nTime - pnode->nTimeConnected > 60) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); pnode->fDisconnect = true; } else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60)) { LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); pnode->fDisconnect = true; } else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) { LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart)); pnode->fDisconnect = true; } else if (!pnode->fSuccessfullyConnected) { LogPrintf("version handshake timeout from %d\n", pnode->id); pnode->fDisconnect = true; } } } { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } } } void CConnman::WakeMessageHandler() { { std::lock_guard<std::mutex> lock(mutexMsgProc); fMsgProcWake = true; } condMsgProc.notify_one(); } #ifdef USE_UPNP void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); const char * multicastif = 0; const char * minissdpdpath = 0; struct UPNPDev * devlist = 0; char lanaddr[64]; #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); #elif MINIUPNPC_API_VERSION < 14 /* miniupnpc 1.6 */ int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); #else /* miniupnpc 1.9.20150730 */ int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); #endif struct UPNPUrls urls; struct IGDdatas data; int r; r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); if (r == 1) { if (fDiscover) { char externalIPAddress[40]; r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); if(r != UPNPCOMMAND_SUCCESS) LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); else { if(externalIPAddress[0]) { CNetAddr resolved; if(LookupHost(externalIPAddress, resolved, false)) { LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str()); AddLocal(resolved, LOCAL_UPNP); } } else LogPrintf("UPnP: GetExternalIPAddress failed.\n"); } } std::string strDesc = "Bitcoin " + FormatFullVersion(); try { while (true) { #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); #else /* miniupnpc 1.6 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); #endif if(r!=UPNPCOMMAND_SUCCESS) LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); else LogPrintf("UPnP Port Mapping successful.\n"); MilliSleep(20*60*1000); // Refresh every 20 minutes } } catch (const boost::thread_interrupted&) { r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); freeUPNPDevlist(devlist); devlist = 0; FreeUPNPUrls(&urls); throw; } } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = 0; if (r != 0) FreeUPNPUrls(&urls); } } void MapPort(bool fUseUPnP) { static boost::thread* upnp_thread = NULL; if (fUseUPnP) { if (upnp_thread) { upnp_thread->interrupt(); upnp_thread->join(); delete upnp_thread; } upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)); } else if (upnp_thread) { upnp_thread->interrupt(); upnp_thread->join(); delete upnp_thread; upnp_thread = NULL; } } #else void MapPort(bool) { // Intentionally left blank. } #endif static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits) { //use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK) if (!data.supportsServiceBitsFiltering || *requiredServiceBits == NODE_NETWORK) { *requiredServiceBits = NODE_NETWORK; return data.host; } // See chainparams.cpp, most dnsseeds only support one or two possible servicebits hostnames return strprintf("x%x.%s", *requiredServiceBits, data.host); } void CConnman::ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute // Avoiding DNS seeds when we don't need them improves user privacy by // creating fewer identifying DNS requests, reduces trust by giving seeds // less influence on the network topology, and reduces traffic to the seeds. if ((addrman.size() > 0) && (!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { if (!interruptNet.sleep_for(std::chrono::seconds(11))) return; LOCK(cs_vNodes); int nRelevant = 0; for (auto pnode : vNodes) { nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices); } if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); return; } } const std::vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); int found = 0; LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) { if (interruptNet) { return; } if (HaveNameProxy()) { AddOneShot(seed.host); } else { std::vector<CNetAddr> vIPs; std::vector<CAddress> vAdd; ServiceFlags requiredServiceBits = nRelevantServices; if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) { BOOST_FOREACH(const CNetAddr& ip, vIPs) { int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } } if (interruptNet) { return; } // TODO: The seed name resolve may fail, yielding an IP of [::], which results in // addrman assigning the same source to results from different seeds. // This should switch to a hard-coded stable dummy IP for each seed name, so that the // resolve is not required at all. if (!vIPs.empty()) { CService seedSource; Lookup(seed.name.c_str(), seedSource, 0, true); addrman.Add(vAdd, seedSource); } } } LogPrintf("%d addresses found from DNS seeds\n", found); } void CConnman::DumpAddresses() { int64_t nStart = GetTimeMillis(); CAddrDB adb; adb.Write(addrman); LogPrint("net", "Flushed %d addresses to peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } void CConnman::DumpData() { DumpAddresses(); DumpBanlist(); } void CConnman::ProcessOneShot() { std::string strDest; { LOCK(cs_vOneShots); if (vOneShots.empty()) return; strDest = vOneShots.front(); vOneShots.pop_front(); } CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { if (!OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true)) AddOneShot(strDest); } } void CConnman::ThreadOpenConnections() { // Connect to specific addresses if (mapMultiArgs.count("-connect") && mapMultiArgs.at("-connect").size() > 0) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); BOOST_FOREACH(const std::string& strAddr, mapMultiArgs.at("-connect")) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } } if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } } // Initiate network connections int64_t nStart = GetTime(); // Minimum time before next feeler connection (in microseconds). int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); while (!interruptNet) { ProcessOneShot(); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; CSemaphoreGrant grant(*semOutbound); if (interruptNet) return; // Add seed nodes if DNS seeds are all down (an infrastructure attack?). if (addrman.size() == 0 && (GetTime() - nStart > 60)) { static bool done = false; if (!done) { LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); CNetAddr local; LookupHost("127.0.0.1", local, false); addrman.Add(convertSeed6(Params().FixedSeeds()), local); done = true; } } // // Choose an address to connect to based on most recently seen // CAddress addrConnect; // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; int nOutboundRelevant = 0; std::set<std::vector<unsigned char> > setConnected; { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if (!pnode->fInbound && !pnode->fAddnode) { // Count the peers that have all relevant services if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) { nOutboundRelevant++; } // Netgroups for inbound and addnode peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup // but inbound and addnode peers do not use our outbound slots. Inbound peers // also have the added issue that they're attacker controlled and could be used // to prevent us from connecting to particular hosts if we used them here. setConnected.insert(pnode->addr.GetGroup()); nOutbound++; } } } // Feeler Connections // // Design goals: // * Increase the number of connectable addresses in the tried table. // // Method: // * Choose a random address from new and attempt to connect to it if we can connect // successfully it is added to tried. // * Start attempting feeler connections only after node finishes making outbound // connections. // * Only make a feeler connection once every few minutes. // bool fFeeler = false; if (nOutbound >= nMaxOutbound) { int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds). if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); fFeeler = true; } else { continue; } } int64_t nANow = GetAdjustedTime(); int nTries = 0; while (!interruptNet) { CAddrInfo addr = addrman.Select(fFeeler); // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) break; // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates // already-connected network ranges, ...) before trying new addrman addresses. nTries++; if (nTries > 100) break; if (IsLimited(addr)) continue; // only connect to full nodes if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) continue; // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up. ServiceFlags nRequiredServices = nRelevantServices; if (nTries >= 40 && nOutbound < (nMaxOutbound >> 1)) { nRequiredServices = REQUIRED_SERVICES; } if ((addr.nServices & nRequiredServices) != nRequiredServices) { continue; } // do not allow non-default ports, unless after 50 invalid addresses selected already if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50) continue; addrConnect = addr; // regardless of the services assumed to be available, only require the minimum if half or more outbound have relevant services if (nOutboundRelevant >= (nMaxOutbound >> 1)) { addrConnect.nServices = REQUIRED_SERVICES; } else { addrConnect.nServices = nRequiredServices; } break; } if (addrConnect.IsValid()) { if (fFeeler) { // Add small amount of random noise before connection to avoid synchronization. int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep))) return; LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString()); } OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler); } } } std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() { std::vector<AddedNodeInfo> ret; std::list<std::string> lAddresses(0); { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) lAddresses.push_back(strAddNode); } // Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService std::map<CService, bool> mapConnected; std::map<std::string, std::pair<bool, CService>> mapConnectedByName; { LOCK(cs_vNodes); for (const CNode* pnode : vNodes) { if (pnode->addr.IsValid()) { mapConnected[pnode->addr] = pnode->fInbound; } std::string addrName = pnode->GetAddrName(); if (!addrName.empty()) { mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr)); } } } BOOST_FOREACH(const std::string& strAddNode, lAddresses) { CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second}); } else { ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first}); } else { ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); } } } return ret; } void CConnman::ThreadOpenAddedConnections() { { LOCK(cs_vAddedNodes); if (mapMultiArgs.count("-addnode")) vAddedNodes = mapMultiArgs.at("-addnode"); } while (true) { CSemaphoreGrant grant(*semAddnode); std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(); bool tried = false; for (const AddedNodeInfo& info : vInfo) { if (!info.fConnected) { if (!grant.TryAcquire()) { // If we've used up our semaphore and need a new one, lets not wait here since while we are waiting // the addednodeinfo state might change. break; } // If strAddedNode is an IP/port, decode it immediately, so // OpenNetworkConnection can detect existing connections to that IP/port. tried = true; CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort())); OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } } // Retry every 60 seconds if a connection was attempted, otherwise two seconds if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2))) return; } } void CConnman::ThreadOpenMasternodeConnections() { // Connecting to specific addresses, no masternode connections available if (IsArgSet("-connect") && mapMultiArgs.at("-connect").size() > 0) return; while (!interruptNet) { if (!interruptNet.sleep_for(std::chrono::milliseconds(1000))) return; std::set<CService> connectedNodes; std::set<uint256> connectedProRegTxHashes; ForEachNode([&](const CNode* pnode) { connectedNodes.emplace(pnode->addr); if (!pnode->verifiedProRegTxHash.IsNull()) { connectedProRegTxHashes.emplace(pnode->verifiedProRegTxHash); } }); auto mnList = deterministicMNManager->GetListAtChainTip(); CSemaphoreGrant grant(*semMasternodeOutbound); if (interruptNet) return; int64_t nANow = GetAdjustedTime(); // NOTE: Process only one pending masternode at a time CService addr; { // don't hold lock while calling OpenMasternodeConnection as cs_main is locked deep inside LOCK2(cs_vNodes, cs_vPendingMasternodes); std::vector<CService> pending; for (const auto& group : masternodeQuorumNodes) { for (const auto& proRegTxHash : group.second) { auto dmn = mnList.GetMN(proRegTxHash); if (!dmn) { continue; } const auto& addr2 = dmn->pdmnState->addr; if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { auto addrInfo = addrman.GetAddressInfo(addr2); // back off trying connecting to an address if we already tried recently if (addrInfo.IsValid() && nANow - addrInfo.nLastTry < 60) { continue; } pending.emplace_back(addr2); } } } if (!vPendingMasternodes.empty()) { auto addr2 = vPendingMasternodes.front(); vPendingMasternodes.erase(vPendingMasternodes.begin()); if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2)) { pending.emplace_back(addr2); } } if (pending.empty()) { // nothing to do, keep waiting continue; } Shuffle(pending.begin(), pending.end(), FastRandomContext()); addr = pending.front(); } OpenMasternodeConnection(CAddress(addr, NODE_NETWORK)); // should be in the list now if connection was opened ForNode(addr, CConnman::AllNodes, [&](CNode* pnode) { if (pnode->fDisconnect) { return false; } grant.MoveTo(pnode->grantMasternodeOutbound); return true; }); } } // if successful, this moves the passed grant to the constructed node bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode, bool fConnectToMasternode) { // // Initiate outbound network connection // if (interruptNet) { return false; } if (!fNetworkActive) { return false; } bool fAllowLocal = fMasternodeMode; if (!pszDest) { // banned or exact match? if (IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) return false; // local and not a connection to itself? if (!fAllowLocal && IsLocal(addrConnect)) return false; } else if (FindNode(std::string(pszDest))) return false; CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, fMasternodeMode); if (!pnode) return false; if (grantOutbound) grantOutbound->MoveTo(pnode->grantOutbound); if (fOneShot) pnode->fOneShot = true; if (fFeeler) pnode->fFeeler = true; if (fConnectToMasternode) pnode->fZnode = true; // Martun: if dandelion is enabled, then send a special transaction // to the new peer to check, if the peer supports dandelion or not. if (GetBoolArg("-dandelion", true)) { LOCK(cs_vNodes); // Dandelion: new outbound connection CNode::vDandelionOutbound.push_back(pnode); const Consensus::Params& consensus = Params().GetConsensus(); if (CNode::vDandelionDestination.size() < consensus.nDandelionMaxDestinations) { CNode::vDandelionDestination.push_back(pnode); } // Dandelion service discovery uint256 dummyHash; dummyHash.SetHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); CInv dummyInv(MSG_DANDELION_TX, dummyHash); pnode->PushInventory(dummyInv); } if (fAddnode) pnode->fAddnode = true; GetNodeSignals().InitializeNode(pnode, *this); { LOCK(cs_vNodes); vNodes.push_back(pnode); } return true; } bool CConnman::OpenMasternodeConnection(const CAddress &addrConnect) { return OpenNetworkConnection(addrConnect, false, NULL, NULL, false, false, false, true); } void CConnman::ThreadMessageHandler() { while (!flagInterruptMsgProc) { std::vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { pnode->AddRef(); } } bool fMoreWork = false; BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect) continue; // Receive messages bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; // Send messages { LOCK(pnode->cs_sendProcessing); GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc); } if (flagInterruptMsgProc) return; } { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } std::unique_lock<std::mutex> lock(mutexMsgProc); if (!fMoreWork) { condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } fMsgProcWake = false; } } bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; // Create socket for listening for incoming connections struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); LogPrintf("%s\n", strError); return false; } SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); if (hListenSocket == INVALID_SOCKET) { strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } if (!IsSelectableSocket(hListenSocket)) { strError = "Error: Couldn't create a listenable socket for incoming connections"; LogPrintf("%s\n", strError); return false; } #ifndef WIN32 #ifdef SO_NOSIGPIPE // Different way of disabling SIGPIPE on BSD setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); #endif // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); // Disable Nagle's algorithm setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int)); #else setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int)); #endif // Set to non-blocking, incoming connections will also inherit this if (!SetSocketNonBlocking(hListenSocket, true)) { strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option // and enable it by default or not. Try to enable it, if possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY #ifdef WIN32 setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); #else setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); #endif #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)); #endif } if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), _(PACKAGE_NAME)); else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; } vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted)); if (addrBind.IsRoutable() && fDiscover && !fWhitelisted) AddLocal(addrBind, LOCAL_BIND); return true; } void Discover(boost::thread_group& threadGroup) { if (!fDiscover) return; #ifdef WIN32 // Get local host IP char pszHostName[256] = ""; if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { std::vector<CNetAddr> vaddr; if (LookupHost(pszHostName, vaddr, 0, true)) { BOOST_FOREACH (const CNetAddr &addr, vaddr) { if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString()); } } } #else // Get local host ip struct ifaddrs* myaddrs; if (getifaddrs(&myaddrs) == 0) { for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue; if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); CNetAddr addr(s4->sin_addr); if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); CNetAddr addr(s6->sin6_addr); if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } } freeifaddrs(myaddrs); } #endif } void CNode::DandelionShuffle() { std::vector<CNode *> vNodes = g_connman->CopyNodeVector([] (const CNode *)->bool {return true;}); { // Iterate through mDandelionRoutes to facilitate bookkeeping mDandelionRoutes.clear(); // Set localDandelionDestination to nulltpr and perform bookkeeping if (localDandelionDestination != nullptr) { localDandelionDestination = nullptr; } // Clear vDandelionDestination // (bookkeeping already done while iterating through mDandelionRoutes) vDandelionDestination.clear(); // Repopulate vDandelionDestination const Consensus::Params& consensus = Params().GetConsensus(); if (vDandelionDestination.size() < consensus.nDandelionMaxDestinations && vDandelionDestination.size() < vDandelionOutbound.size()) { std::vector<CNode*> candidateDestinations; for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) { bool eligibleCandidate = true; for (auto iterj = vDandelionDestination.begin(); iterj != vDandelionDestination.end(); ++iterj) { if (*iteri==*iterj) { eligibleCandidate = false; break; } } if (eligibleCandidate) { candidateDestinations.push_back(*iteri); } ++iteri; } // Sample "vDandelionDestination.size() - DANDELION_MAX_DESTINATIONS" destinations // if there are that many to choose from. FastRandomContext rng; const Consensus::Params& consensus = Params().GetConsensus(); while (vDandelionDestination.size() < consensus.nDandelionMaxDestinations && vDandelionDestination.size() < vDandelionOutbound.size() && candidateDestinations.size() > 0) { int rand_index = rng.rand32() % candidateDestinations.size(); vDandelionDestination.push_back(candidateDestinations[rand_index]); candidateDestinations.erase(candidateDestinations.begin() + rand_index); } } // Generate new routes for (auto pnode : vDandelionInbound) { CNode* pto = CNode::SelectFromDandelionDestinations(); if (pto != nullptr) { mDandelionRoutes.insert(std::make_pair(pnode, pto)); } } localDandelionDestination = CNode::SelectFromDandelionDestinations(); } g_connman->ReleaseNodeVector(vNodes); } void CConnman::ThreadDandelionShuffle() { int64_t nNextDandelionShuffle = 0; while (!interruptNet) { if (GetTimeMicros() > nNextDandelionShuffle) { CNode::DandelionShuffle(); const Consensus::Params& consensus = Params().GetConsensus(); nNextDandelionShuffle = PoissonNextSend( GetTimeMicros(), consensus.nDandelionShuffleInterval); // Sleep for 1 second until the next shuffle time. // Sleeping for DANDELION_SHUFFLE_INTERVAL seconds at once // results to not being able to close firo. int time_to_sleep = (nNextDandelionShuffle - GetTimeMicros()) / 1000; while (time_to_sleep > 0) { if (!interruptNet.sleep_for( std::chrono::milliseconds(std::min(time_to_sleep, 1000)))) { return; } boost::this_thread::interruption_point(); time_to_sleep = (nNextDandelionShuffle - GetTimeMicros()) / 1000; } } } } void CConnman::SetNetworkActive(bool active) { if (fDebug) { LogPrint("net", "SetNetworkActive: %s\n", active); } if (!active) { fNetworkActive = false; LOCK(cs_vNodes); // Close sockets to all nodes BOOST_FOREACH(CNode* pnode, vNodes) { pnode->CloseSocketDisconnect(); } } else { fNetworkActive = true; } uiInterface.NotifyNetworkActiveChanged(fNetworkActive); } CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In) { fNetworkActive = true; setBannedIsDirty = false; fAddressesInitialized = false; nLastNodeId = 0; nSendBufferMaxSize = 0; nReceiveFloodSize = 0; semOutbound = NULL; semAddnode = NULL; semMasternodeOutbound = NULL; nMaxConnections = 0; nMaxOutbound = 0; nMaxAddnode = 0; nBestHeight = 0; clientInterface = NULL; flagInterruptMsgProc = false; } NodeId CConnman::GetNewNodeId() { return nLastNodeId.fetch_add(1, std::memory_order_relaxed); } bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options connOptions) { nTotalBytesRecv = 0; nTotalBytesSent = 0; nMaxOutboundTotalBytesSentInCycle = 0; nMaxOutboundCycleStartTime = 0; nRelevantServices = connOptions.nRelevantServices; nLocalServices = connOptions.nLocalServices; nMaxConnections = connOptions.nMaxConnections; nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections); nMaxAddnode = connOptions.nMaxAddnode; nMaxFeeler = connOptions.nMaxFeeler; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; SetBestHeight(connOptions.nBestHeight); clientInterface = connOptions.uiInterface; if (clientInterface) clientInterface->InitMessage(_("Loading addresses...")); // Load addresses from
{'commit': 'diff --git a/src/net.cpp b/src/net.cpp\nindex e5f67262c..45a06a105 100644\n--- a/src/net.cpp\n+++ b/src/net.cpp\n@@ -100,6 +100,7 @@ NodeId nLastNodeId = 0;\n CCriticalSection cs_nLastNodeId;\n \n static CSemaphore *semOutbound = NULL;\n+boost::condition_variable messageHandlerCondition;\n \n // Signals for message handling\n static CNodeSignals g_signals;\n@@ -532,8 +533,10 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)\n         pch += handled;\n         nBytes -= handled;\n \n-        if (msg.complete())\n+        if (msg.complete()) {\n             msg.nTime = GetTimeMicros();\n+            messageHandlerCondition.notify_one();\n+        }\n     }\n \n     return true;\n@@ -1358,6 +1361,9 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu\n \n void ThreadMessageHandler()\n {\n+    boost::mutex condition_mutex;\n+    boost::unique_lock<boost::mutex> lock(condition_mutex);\n+    \n     SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);\n     while (true)\n     {\n@@ -1417,7 +1423,7 @@ void ThreadMessageHandler()\n         }\n \n         if (fSleep)\n-            MilliSleep(100);\n+            messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100));\n     }\n }\n \n', 'commit_subject': 'Merge pull request #5971', 'repo_name': 'zcoinofficial/zcoin', 'committer': {'name': 'Wladimir J. van der Laan', 'email': 'bbc07dc96bb50bf6868a2ddfc4dae14fbed703d9@gmail.com', 'time_sec': '1430210506', 'tz_offset': '120', 'date': {'seconds': '1430210506'}}, 'commit_tree': '0e6d9c95890af64b4f6b7a3af45214013e90e483', 'commit_message': 'Merge pull request #5971\n\n351593b replace absolute sleep with conditional wait (pstratem)\n', 'language_name': 'Perl', 'license': 'mit', 'hunks': '@@ -100,6 +100,7 @@ NodeId nLastNodeId = 0;\n CCriticalSection cs_nLastNodeId;\n \n static CSemaphore *semOutbound = NULL;\n+boost::condition_variable messageHandlerCondition;\n \n // Signals for message handling\n static CNodeSignals g_signals;\n@@ -532,8 +533,10 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)\n         pch += handled;\n         nBytes -= handled;\n \n-        if (msg.complete())\n+        if (msg.complete()) {\n             msg.nTime = GetTimeMicros();\n+            messageHandlerCondition.notify_one();\n+        }\n     }\n \n     return true;\n@@ -1358,6 +1361,9 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu\n \n void ThreadMessageHandler()\n {\n+    boost::mutex condition_mutex;\n+    boost::unique_lock<boost::mutex> lock(condition_mutex);\n+    \n     SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);\n     while (true)\n     {\n@@ -1417,7 +1423,7 @@ void ThreadMessageHandler()\n         }\n \n         if (fSleep)\n-            MilliSleep(100);\n+            messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100));\n     }\n }\n \n', 'addition_count': 8, 'deletion_count': 2, 'src_file': 'a/src/net.cpp', 'tgt_file': 'b/src/net.cpp', 'file_extension': '.cpp', 'before_file': '// Copyright (c) 2009-2010 Satoshi Nakamoto\n// Copyright (c) 2009-2016 The Bitcoin Core developers\n// Distributed under the MIT software license, see the accompanying\n// file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n#if defined(HAVE_CONFIG_H)\n#include "config/bitcoin-config.h"\n#endif\n\n#include "validation.h"\n#include "net.h"\n\n#include "addrman.h"\n#include "chainparams.h"\n#include "clientversion.h"\n#include "consensus/consensus.h"\n#include "crypto/common.h"\n#include "crypto/sha256.h"\n#include "hash.h"\n#include "primitives/transaction.h"\n#include "netbase.h"\n#include "scheduler.h"\n#include "ui_interface.h"\n#include "utilstrencodings.h"\n#include "consensus/validation.h"\n#include "txmempool.h"\n#include "./consensus/validation.h"\n\n#include "masternode-sync.h"\n#include "llmq/quorums_instantsend.h"\n#include "evo/mnauth.h"\n\n#ifdef WIN32\n#include <string.h>\n#else\n#include <fcntl.h>\n#endif\n\n#ifdef USE_UPNP\n#include <miniupnpc/miniupnpc.h>\n#include <miniupnpc/miniwget.h>\n#include <miniupnpc/upnpcommands.h>\n#include <miniupnpc/upnperrors.h>\n#endif\n\n\n#include <math.h>\n\n// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)\n#define DUMP_ADDRESSES_INTERVAL 900\n\n// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.\n#define FEELER_SLEEP_WINDOW 1\n\n#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)\n#define MSG_NOSIGNAL 0\n#endif\n\n// Fix for ancient MinGW versions, that don\'t have defined these in ws2tcpip.h.\n// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version.\n#ifdef WIN32\n#ifndef PROTECTION_LEVEL_UNRESTRICTED\n#define PROTECTION_LEVEL_UNRESTRICTED 10\n#endif\n#ifndef IPV6_PROTECTION_LEVEL\n#define IPV6_PROTECTION_LEVEL 23\n#endif\n#endif\n\n\nnamespace {\n    const int MAX_OUTBOUND_CONNECTIONS = 8;\n    const int MAX_FEELER_CONNECTIONS = 1;\n\n    struct ListenSocket {\n        SOCKET socket;\n        bool whitelisted;\n\n        ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}\n    };\n}\n\nconstexpr const CConnman::CFullyConnectedOnly CConnman::FullyConnectedOnly;\nconstexpr const CConnman::CAllNodes CConnman::AllNodes;\n\nconst static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";\nstatic const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]\nstatic const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]\n\n// Public Dandelion fields.\n\n// All transactions embargoed by dandelion.\nstd::map<uint256, int64_t> CNode::mDandelionEmbargo;\n\n// Inbound connections. Transactions from each connection\n// are broadcast to one of 2 dandelion destinations.\nstd::vector<CNode*> CNode::vDandelionInbound;\n\n// All of our outbound destinations.\nCCriticalSection cs_nLastNodeId;\n\nstatic CSemaphore *semOutbound = NULL;\n\n// Signals for message handling\nstatic CNodeSignals g_signals;\nCNode* CNode::localDandelionDestination = nullptr;\n\n// All txn are put in the stempool in stem phase.\n// After getting relayed they are moved to mempool.\nextern CTxPoolAggregate txpools;\n\n/** Services this node implementation cares about */\nServiceFlags nRelevantServices = NODE_NETWORK;\n\n//\n// Global state variables\n//\nbool fDiscover = true;\nbool fListen = true;\nbool fRelayTxes = true;\nCCriticalSection cs_mapLocalHost;\nstd::map<CNetAddr, LocalServiceInfo> mapLocalHost;\nstatic bool vfLimited[NET_MAX] = {};\nstd::string strSubVersion;\n\nlimitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);\n\n// Signals for message handling\nstatic CNodeSignals g_signals;\nCNodeSignals& GetNodeSignals() { return g_signals; }\n\nvoid CConnman::AddOneShot(const std::string& strDest)\n{\n    LOCK(cs_vOneShots);\n    vOneShots.push_back(strDest);\n}\n\nunsigned short GetListenPort()\n{\n    return (unsigned short)(GetArg("-port", Params().GetDefaultPort()));\n}\n\n// find \'best\' local address for a particular peer\nbool GetLocal(CService& addr, const CNetAddr *paddrPeer)\n{\n    if (!fListen)\n        return false;\n\n    int nBestScore = -1;\n    int nBestReachability = -1;\n    {\n        LOCK(cs_mapLocalHost);\n        for (std::map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)\n        {\n            int nScore = (*it).second.nScore;\n            int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);\n            if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))\n            {\n                addr = CService((*it).first, (*it).second.nPort);\n                nBestReachability = nReachability;\n                nBestScore = nScore;\n            }\n        }\n    }\n    return nBestScore >= 0;\n}\n\n//! Convert the pnSeeds6 array into usable address objects.\nstatic std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn)\n{\n    // It\'ll only connect to one or two seed nodes because once it connects,\n    // it\'ll get a pile of addresses with newer timestamps.\n    // Seed nodes are given a random \'last seen time\' of between one and two\n    // weeks ago.\n    const int64_t nOneWeek = 7*24*60*60;\n    std::vector<CAddress> vSeedsOut;\n    vSeedsOut.reserve(vSeedsIn.size());\n    for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i)\n    {\n        struct in6_addr ip;\n        memcpy(&ip, i->addr, sizeof(ip));\n        CAddress addr(CService(ip, i->port), NODE_NETWORK);\n        addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;\n        vSeedsOut.push_back(addr);\n    }\n    return vSeedsOut;\n}\n\n// get best local address for a particular peer as a CAddress\n// Otherwise, return the unroutable 0.0.0.0 but filled in with\n// the normal parameters, since the IP may be changed to a useful\n// one by discovery.\nCAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)\n{\n    CAddress ret(CService(CNetAddr(),GetListenPort()), nLocalServices);\n    CService addr;\n    if (GetLocal(addr, paddrPeer))\n    {\n        ret = CAddress(addr, nLocalServices);\n    }\n    ret.nTime = GetAdjustedTime();\n    return ret;\n}\n\nint GetnScore(const CService& addr)\n{\n    LOCK(cs_mapLocalHost);\n    if (mapLocalHost.count(addr) == LOCAL_NONE)\n        return 0;\n    return mapLocalHost[addr].nScore;\n}\n\n// Is our peer\'s addrLocal potentially useful as an external IP source?\nbool IsPeerAddrLocalGood(CNode *pnode)\n{\n    CService addrLocal = pnode->GetAddrLocal();\n    return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() &&\n           !IsLimited(addrLocal.GetNetwork());\n}\n\n// pushes our own address to a peer\nvoid AdvertiseLocal(CNode *pnode)\n{\n    if (fListen && pnode->fSuccessfullyConnected)\n    {\n        CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());\n        // If discovery is enabled, sometimes give our peer the address it\n        // tells us that it sees us as in case it has a better idea of our\n        // address than we do.\n        if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||\n             GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))\n        {\n            addrLocal.SetIP(pnode->GetAddrLocal());\n        }\n        if (addrLocal.IsRoutable())\n        {\n            LogPrint("net", "AdvertiseLocal: advertising address %s\\n", addrLocal.ToString());\n            FastRandomContext insecure_rand;\n            pnode->PushAddress(addrLocal, insecure_rand);\n        }\n    }\n}\n\n// learn a new local address\nbool AddLocal(const CService& addr, int nScore)\n{\n    if (!addr.IsRoutable())\n        return false;\n\n    if (!fDiscover && nScore < LOCAL_MANUAL)\n        return false;\n\n    if (IsLimited(addr))\n        return false;\n\n    LogPrintf("AddLocal(%s,%i)\\n", addr.ToString(), nScore);\n\n    {\n        LOCK(cs_mapLocalHost);\n        bool fAlready = mapLocalHost.count(addr) > 0;\n        LocalServiceInfo &info = mapLocalHost[addr];\n        if (!fAlready || nScore >= info.nScore) {\n            info.nScore = nScore + (fAlready ? 1 : 0);\n            info.nPort = addr.GetPort();\n        }\n    }\n\n    return true;\n}\n\nbool AddLocal(const CNetAddr &addr, int nScore)\n{\n    return AddLocal(CService(addr, GetListenPort()), nScore);\n}\n\nbool RemoveLocal(const CService& addr)\n{\n    LOCK(cs_mapLocalHost);\n    LogPrintf("RemoveLocal(%s)\\n", addr.ToString());\n    mapLocalHost.erase(addr);\n    return true;\n}\n\n/** Make a particular network entirely off-limits (no automatic connects to it) */\nvoid SetLimited(enum Network net, bool fLimited)\n{\n    if (net == NET_UNROUTABLE)\n        return;\n    LOCK(cs_mapLocalHost);\n    vfLimited[net] = fLimited;\n}\n\nbool IsLimited(enum Network net)\n{\n    LOCK(cs_mapLocalHost);\n    return vfLimited[net];\n}\n\nbool IsLimited(const CNetAddr &addr)\n{\n    return IsLimited(addr.GetNetwork());\n}\n\n/** vote for a local address */\nbool SeenLocal(const CService& addr)\n{\n    {\n        LOCK(cs_mapLocalHost);\n        if (mapLocalHost.count(addr) == 0)\n            return false;\n        mapLocalHost[addr].nScore++;\n    }\n    return true;\n}\n\n\n/** check whether a given address is potentially local */\nbool IsLocal(const CService& addr)\n{\n    LOCK(cs_mapLocalHost);\n    return mapLocalHost.count(addr) > 0;\n}\n\n/** check whether a given network is one we can probably connect to */\nbool IsReachable(enum Network net)\n{\n    LOCK(cs_mapLocalHost);\n    return !vfLimited[net];\n}\n\n/** check whether a given address is in a network we can probably connect to */\nbool IsReachable(const CNetAddr& addr)\n{\n    enum Network net = addr.GetNetwork();\n    return IsReachable(net);\n}\n\n\nCNode* CConnman::FindNode(const CNetAddr& ip)\n{\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes)\n        if ((CNetAddr)pnode->addr == ip)\n            return (pnode);\n    return NULL;\n}\n\nCNode* CConnman::FindNode(const CSubNet& subNet)\n{\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes)\n    if (subNet.Match((CNetAddr)pnode->addr))\n        return (pnode);\n    return NULL;\n}\n\nCNode* CConnman::FindNode(const std::string& addrName)\n{\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes) {\n        if (pnode->GetAddrName() == addrName) {\n            return (pnode);\n        }\n    }\n    return NULL;\n}\n\nCNode* CConnman::FindNode(const CService& addr)\n{\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes)\n        if ((CService)pnode->addr == addr)\n            return (pnode);\n    return NULL;\n}\n\nbool CConnman::CheckIncomingNonce(uint64_t nonce)\n{\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes) {\n        if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce)\n            return false;\n    }\n    return true;\n}\n\nCNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool fAllowLocal)\n{\n    if (pszDest == NULL) {\n        // we clean znode connections in CZnodeMan::ProcessZnodeConnections()\n        // so should be safe to skip this and connect to local Hot MN on CActiveZnode::ManageState()\n        if (!fAllowLocal && IsLocal(addrConnect))\n            return NULL;\n        LOCK(cs_vNodes);\n        // Look for an existing connection\n        CNode* pnode = FindNode((CService)addrConnect);\n        if (pnode)\n        {\n            LogPrintf("Failed to open new connection, already connected\\n");\n            return NULL;\n        }\n    }\n\n    /// debug print\n    LogPrint("net", "trying connection %s lastseen=%.1fhrs\\n",\n        pszDest ? pszDest : addrConnect.ToString(),\n        pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);\n\n    // Connect\n    SOCKET hSocket;\n    bool proxyConnectionFailed = false;\n    if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) :\n                  ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed))\n    {\n        if (!IsSelectableSocket(hSocket)) {\n            LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\\n");\n            CloseSocket(hSocket);\n            return NULL;\n        }\n\n        if (pszDest && addrConnect.IsValid()) {\n            // It is possible that we already have a connection to the IP/port pszDest resolved to.\n            // In that case, drop the connection that was just created, and return the existing CNode instead.\n            // Also store the name we used to connect in that CNode, so that future FindNode() calls to that\n            // name catch this early.\n            LOCK(cs_vNodes);\n            CNode* pnode = FindNode((CService)addrConnect);\n            if (pnode)\n            {\n                pnode->MaybeSetAddrName(std::string(pszDest));\n                CloseSocket(hSocket);\n                LogPrintf("Failed to open new connection, already connected\\n");\n                return NULL;\n            }\n        }\n\n        addrman.Attempt(addrConnect, fCountFailure);\n\n        // Add node\n        NodeId id = GetNewNodeId();\n        uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();\n        CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, pszDest ? pszDest : "", false);\n        pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);\n        pnode->AddRef();\n\n        return pnode;\n    } else if (!proxyConnectionFailed) {\n        // If connecting to the node failed, and failure is not caused by a problem connecting to\n        // the proxy, mark this as an attempt.\n        addrman.Attempt(addrConnect, fCountFailure);\n    }\n\n    return NULL;\n}\n\nvoid CConnman::DumpBanlist()\n{\n    SweepBanned(); // clean unused entries (if bantime has expired)\n\n    if (!BannedSetIsDirty())\n        return;\n\n    int64_t nStart = GetTimeMillis();\n\n    CBanDB bandb;\n    banmap_t banmap;\n    GetBanned(banmap);\n    if (bandb.Write(banmap)) {\n        SetBannedSetDirty(false);\n    }\n\n    LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat  %dms\\n",\n        banmap.size(), GetTimeMillis() - nStart);\n}\n\nvoid CNode::CloseSocketDisconnect()\n{\n    fDisconnect = true;\n    LOCK(cs_hSocket);\n    if (hSocket != INVALID_SOCKET)\n    {\n        LogPrint("net", "disconnecting peer=%d\\n", id);\n        CloseSocket(hSocket);\n    }\n}\n\nvoid CConnman::ClearBanned()\n{\n    {\n        LOCK(cs_setBanned);\n        setBanned.clear();\n        setBannedIsDirty = true;\n    }\n    DumpBanlist(); //store banlist to disk\n    if(clientInterface)\n        clientInterface->BannedListChanged();\n}\n\nbool CConnman::IsBanned(CNetAddr ip)\n{\n    bool fResult = false;\n    {\n        LOCK(cs_setBanned);\n        for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++)\n        {\n            CSubNet subNet = (*it).first;\n            CBanEntry banEntry = (*it).second;\n\n            if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil)\n                fResult = true;\n        }\n    }\n    return fResult;\n}\n\nbool CConnman::IsBanned(CSubNet subnet)\n{\n    bool fResult = false;\n    {\n        LOCK(cs_setBanned);\n        banmap_t::iterator i = setBanned.find(subnet);\n        if (i != setBanned.end())\n        {\n            CBanEntry banEntry = (*i).second;\n            if (GetTime() < banEntry.nBanUntil)\n                fResult = true;\n        }\n    }\n    return fResult;\n}\n\n        pch += handled;\n        nBytes -= handled;\n\n        if (msg.complete())\n            msg.nTime = GetTimeMicros();\n    }\n\n    return true;\n        bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME);\n        sinceUnixEpoch = false;\n    }\n    banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;\n\n    {\n        LOCK(cs_setBanned);\n        if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) {\n            setBanned[subNet] = banEntry;\n            setBannedIsDirty = true;\n        }\n        else\n            return;\n    }\n    if(clientInterface)\n        clientInterface->BannedListChanged();\n    {\n        LOCK(cs_vNodes);\n        BOOST_FOREACH(CNode* pnode, vNodes) {\n            if (subNet.Match((CNetAddr)pnode->addr))\n                pnode->fDisconnect = true;\n        }\n    }\n    if(banReason == BanReasonManuallyAdded)\n        DumpBanlist(); //store banlist to disk immediately if user requested ban\n}\n\nbool CConnman::Unban(const CNetAddr &addr) {\n    CSubNet subNet(addr);\n    return Unban(subNet);\n}\n\nbool CConnman::Unban(const CSubNet &subNet) {\n    {\n        LOCK(cs_setBanned);\n        if (!setBanned.erase(subNet))\n            return false;\n        setBannedIsDirty = true;\n    }\n    if(clientInterface)\n        clientInterface->BannedListChanged();\n    DumpBanlist(); //store banlist to disk immediately\n    return true;\n}\n\nvoid CConnman::GetBanned(banmap_t &banMap)\n{\n    LOCK(cs_setBanned);\n    // Sweep the banlist so expired bans are not returned\n    SweepBanned();\n    banMap = setBanned; //create a thread safe copy\n}\n\nvoid CConnman::SetBanned(const banmap_t &banMap)\n{\n    LOCK(cs_setBanned);\n    setBanned = banMap;\n    setBannedIsDirty = true;\n}\n\nvoid CConnman::SweepBanned()\n{\n    int64_t now = GetTime();\n\n    LOCK(cs_setBanned);\n    banmap_t::iterator it = setBanned.begin();\n    while(it != setBanned.end())\n    {\n        CSubNet subNet = (*it).first;\n        CBanEntry banEntry = (*it).second;\n        if(now > banEntry.nBanUntil)\n        {\n            setBanned.erase(it++);\n            setBannedIsDirty = true;\n            LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\\n", __func__, subNet.ToString());\n        }\n        else\n            ++it;\n    }\n}\n\nbool CConnman::BannedSetIsDirty()\n{\n    LOCK(cs_setBanned);\n    return setBannedIsDirty;\n}\n\nvoid CConnman::SetBannedSetDirty(bool dirty)\n{\n    LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag\n    setBannedIsDirty = dirty;\n}\n\n\nbool CConnman::IsWhitelistedRange(const CNetAddr &addr) {\n    LOCK(cs_vWhitelistedRange);\n    BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {\n        if (subnet.Match(addr))\n            return true;\n    }\n    return false;\n}\n\nvoid CConnman::AddWhitelistedRange(const CSubNet &subnet) {\n    LOCK(cs_vWhitelistedRange);\n    vWhitelistedRange.push_back(subnet);\n}\n\n\nstd::string CNode::GetAddrName() const {\n    LOCK(cs_addrName);\n    return addrName;\n}\n\nvoid CNode::MaybeSetAddrName(const std::string& addrNameIn) {\n    LOCK(cs_addrName);\n    if (addrName.empty()) {\n        addrName = addrNameIn;\n    }\n}\n\nCService CNode::GetAddrLocal() const {\n    LOCK(cs_addrLocal);\n    return addrLocal;\n}\n\nvoid CNode::SetAddrLocal(const CService& addrLocalIn) {\n    LOCK(cs_addrLocal);\n    if (addrLocal.IsValid()) {\n        error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString());\n    } else {\n        addrLocal = addrLocalIn;\n    }\n}\n\n#undef X\n#define X(name) stats.name = name\nvoid CNode::copyStats(CNodeStats &stats)\n{\n    stats.nodeid = this->GetId();\n    X(nServices);\n    X(addr);\n    {\n        LOCK(cs_filter);\n        X(fRelayTxes);\n    }\n    X(nLastSend);\n    X(nLastRecv);\n    X(nTimeConnected);\n    X(nTimeOffset);\n    stats.addrName = GetAddrName();\n    X(nVersion);\n    {\n        LOCK(cs_SubVer);\n        X(cleanSubVer);\n    }\n    X(fInbound);\n    X(fAddnode);\n    X(nStartingHeight);\n    {\n        LOCK(cs_vSend);\n        X(mapSendBytesPerMsgCmd);\n        X(nSendBytes);\n    }\n    {\n        LOCK(cs_vRecv);\n        X(mapRecvBytesPerMsgCmd);\n        X(nRecvBytes);\n    }\n    X(fWhitelisted);\n\n    // It is common for nodes with good ping times to suddenly become lagged,\n    // due to a new block arriving or other large transfer.\n    // Merely reporting pingtime might fool the caller into thinking the node was still responsive,\n    // since pingtime does not update until the ping is complete, which might take a while.\n    // So, if a ping is taking an unusually long time in flight,\n    // the caller can immediately detect that this is happening.\n    int64_t nPingUsecWait = 0;\n    if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) {\n        nPingUsecWait = GetTimeMicros() - nPingUsecStart;\n    }\n\n    // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)\n    stats.dPingTime = (((double)nPingUsecTime) / 1e6);\n    stats.dMinPing  = (((double)nMinPingUsecTime) / 1e6);\n    stats.dPingWait = (((double)nPingUsecWait) / 1e6);\n\n    // Leave string empty if addrLocal invalid (not filled in yet)\n    CService addrLocalUnlocked = GetAddrLocal();\n    stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";\n\n    {\n        LOCK(cs_mnauth);\n        X(verifiedProRegTxHash);\n    }\n}\n#undef X\n\nbool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)\n{\n    complete = false;\n    int64_t nTimeMicros = GetTimeMicros();\n    LOCK(cs_vRecv);\n    nLastRecv = nTimeMicros / 1000000;\n    nRecvBytes += nBytes;\n    while (nBytes > 0) {\n\n        // get current incomplete message, or create a new one\n        if (vRecvMsg.empty() ||\n            vRecvMsg.back().complete())\n            vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));\n\n        CNetMessage& msg = vRecvMsg.back();\n\n        // absorb network data\n        int handled;\n        if (!msg.in_data) {\n            handled = msg.readHeader(pch, nBytes);\n            if (msg.in_data && nTimeFirstMessageReceived == 0) {\n                if (fSuccessfullyConnected) {\n                    // First message after VERSION/VERACK.\n                    // We record the time when the header is fully received and not when the full message is received.\n                    // otherwise a peer might send us a very large message as first message after VERSION/VERACK and fill\n                    // up our memory with multiple parallel connections doing this.\n                    nTimeFirstMessageReceived = nTimeMicros;\n                    fFirstMessageIsMNAUTH = msg.hdr.GetCommand() == NetMsgType::MNAUTH;\n                } else {\n                    // We\'re still in the VERSION/VERACK handshake process, so any message received in this state is\n                    // expected to be very small. This protects against attackers filling up memory by sending oversized\n                    // VERSION messages while the incoming connection is still protected against eviction\n                    if (msg.hdr.nMessageSize > 1024) {\n                        LogPrint("net", "Oversized VERSION/VERACK message from peer=%i, disconnecting\\n", GetId());\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n            handled = msg.readData(pch, nBytes);\n\n        if (handled < 0)\n                return false;\n\n        if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {\n            LogPrint("net", "Oversized message from peer=%i, disconnecting\\n", GetId());\n            return false;\n        }\n\n        pch += handled;\n        nBytes -= handled;\n\n        if (msg.complete()) {\n\n            //store received bytes per message command\n            //to prevent a memory DOS, only allow valid commands\n            mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.hdr.pchCommand);\n            if (i == mapRecvBytesPerMsgCmd.end())\n                i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);\n            assert(i != mapRecvBytesPerMsgCmd.end());\n            i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE;\n\n            msg.nTime = nTimeMicros;\n            complete = true;\n        }\n    }\n\n    return true;\n}\n\nvoid CNode::SetSendVersion(int nVersionIn)\n{\n    // Send version may only be changed in the version message, and\n    // only one version message is allowed per session. We can therefore\n    // treat this value as const and even atomic as long as it\'s only used\n    // once a version message has been successfully processed. Any attempt to\n    // set this twice is an error.\n    if (nSendVersion != 0) {\n        error("Send version already set for node: %i. Refusing to change from %i to %i", id, nSendVersion, nVersionIn);\n    } else {\n        nSendVersion = nVersionIn;\n    }\n}\n\nint CNode::GetSendVersion() const\n{\n    // The send version should always be explicitly set to\n    // INIT_PROTO_VERSION rather than using this value until SetSendVersion\n    // has been called.\n    if (nSendVersion == 0) {\n        error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION);\n        return INIT_PROTO_VERSION;\n    }\n    return nSendVersion;\n}\n\n\nint CNetMessage::readHeader(const char *pch, unsigned int nBytes)\n{\n    // copy data to temporary parsing buffer\n    unsigned int nRemaining = 24 - nHdrPos;\n    unsigned int nCopy = std::min(nRemaining, nBytes);\n\n    memcpy(&hdrbuf[nHdrPos], pch, nCopy);\n    nHdrPos += nCopy;\n\n    // if header incomplete, exit\n    if (nHdrPos < 24)\n        return nCopy;\n\n    // deserialize to CMessageHeader\n    try {\n        hdrbuf >> hdr;\n    }\n    catch (const std::exception&) {\n        return -1;\n    }\n\n    // reject messages larger than MAX_SIZE\n    if (hdr.nMessageSize > MAX_SIZE)\n            return -1;\n\n    // switch state to reading message data\n    in_data = true;\n\n    return nCopy;\n}\n\nint CNetMessage::readData(const char *pch, unsigned int nBytes)\n{\n    unsigned int nRemaining = hdr.nMessageSize - nDataPos;\n    unsigned int nCopy = std::min(nRemaining, nBytes);\n\n    if (vRecv.size() < nDataPos + nCopy) {\n        // Allocate up to 256 KiB ahead, but never more than the total message size.\n        vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024));\n    }\n\n    hasher.Write((const unsigned char*)pch, nCopy);\n    memcpy(&vRecv[nDataPos], pch, nCopy);\n    nDataPos += nCopy;\n\n    return nCopy;\n}\n\nconst uint256& CNetMessage::GetMessageHash() const\n{\n    assert(complete());\n    if (data_hash.IsNull())\n        hasher.Finalize(data_hash.begin());\n    return data_hash;\n}\n\n\n\n\n\n\n\n\n\n// requires LOCK(cs_vSend)\nsize_t CConnman::SocketSendData(CNode *pnode) const\n{\n    auto it = pnode->vSendMsg.begin();\n    size_t nSentSize = 0;\n\n    while (it != pnode->vSendMsg.end()) {\n        const auto &data = *it;\n        assert(data.size() > pnode->nSendOffset);\n        int nBytes = 0;\n        {\n            LOCK(pnode->cs_hSocket);\n            if (pnode->hSocket == INVALID_SOCKET)\n                break;\n            nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);\n        }\n        if (nBytes > 0) {\n            pnode->nLastSend = GetSystemTimeInSeconds();\n            pnode->nSendBytes += nBytes;\n            pnode->nSendOffset += nBytes;\n            nSentSize += nBytes;\n            if (pnode->nSendOffset == data.size()) {\n                pnode->nSendOffset = 0;\n                pnode->nSendSize -= data.size();\n                pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize;\n                it++;\n            } else {\n                // could not send full message; stop sending more\n                break;\n            }\n        } else {\n            if (nBytes < 0) {\n                // error\n                int nErr = WSAGetLastError();\n                if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)\n                {\n                    LogPrintf("socket send error %s\\n", NetworkErrorString(nErr));\n                    pnode->CloseSocketDisconnect();\n                }\n            }\n            // couldn\'t send anything at all\n            break;\n        }\n    }\n\n    if (it == pnode->vSendMsg.end()) {\n        assert(pnode->nSendOffset == 0);\n        assert(pnode->nSendSize == 0);\n    }\n    pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it);\n    return nSentSize;\n}\n\nstruct NodeEvictionCandidate\n{\n    NodeId id;\n    int64_t nTimeConnected;\n    int64_t nMinPingUsecTime;\n    int64_t nLastBlockTime;\n    int64_t nLastTXTime;\n    bool fRelevantServices;\n    bool fRelayTxes;\n    bool fBloomFilter;\n    CAddress addr;\n    uint64_t nKeyedNetGroup;\n};\n\nstatic bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)\n{\n    return a.nMinPingUsecTime > b.nMinPingUsecTime;\n}\n\nstatic bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)\n{\n    return a.nTimeConnected > b.nTimeConnected;\n}\n\nstatic bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {\n    return a.nKeyedNetGroup < b.nKeyedNetGroup;\n}\n\nstatic bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)\n{\n    // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.\n    if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;\n    if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;\n    return a.nTimeConnected > b.nTimeConnected;\n}\n\nstatic bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)\n{\n    // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn.\n    if (a.nLastTXTime != b.nLastTXTime) return a.nLastTXTime < b.nLastTXTime;\n    if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes;\n    if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter;\n    return a.nTimeConnected > b.nTimeConnected;\n}\n\n/** Try to find a connection to evict when the node is full.\n *  Extreme care must be taken to avoid opening the node to attacker\n *   triggered network partitioning.\n *  The strategy used here is to protect a small number of peers\n *   for each of several distinct characteristics which are difficult\n *   to forge.  In order to partition a node the attacker must be\n *   simultaneously better at all of them than honest peers.\n */\nbool CConnman::AttemptToEvictConnection()\n{\n    std::vector<NodeEvictionCandidate> vEvictionCandidates;\n    {\n        LOCK(cs_vNodes);\n\n        BOOST_FOREACH(CNode *node, vNodes) {\n            if (node->fWhitelisted)\n                continue;\n            if (!node->fInbound)\n                continue;\n            if (node->fDisconnect)\n                continue;\n\n            if (fMasternodeMode) {\n                // This handles eviction protected nodes. Nodes are always protected for a short time after the connection\n                // was accepted. This short time is meant for the VERSION/VERACK exchange and the possible MNAUTH that might\n                // follow when the incoming connection is from another masternode. When another message then MNAUTH\n                // is received after VERSION/VERACK, the protection is lifted immediately.\n                bool isProtected = GetSystemTimeInSeconds() - node->nTimeConnected < INBOUND_EVICTION_PROTECTION_TIME;\n                if (node->nTimeFirstMessageReceived != 0 && !node->fFirstMessageIsMNAUTH) {\n                    isProtected = false;\n                }\n                // if MNAUTH was valid, the node is always protected (and at the same time not accounted when\n                // checking incoming connection limits)\n                if (!node->verifiedProRegTxHash.IsNull()) {\n                    isProtected = true;\n                }\n                if (isProtected) {\n                    continue;\n                }\n            }\n\n            NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime,\n                                               node->nLastBlockTime, node->nLastTXTime,\n                                               (node->nServices & nRelevantServices) == nRelevantServices,\n                                               node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup};\n            vEvictionCandidates.push_back(candidate);\n        }\n    }\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Protect connections with certain characteristics\n\n    // Deterministically select 4 peers to protect by netgroup.\n    // An attacker cannot predict which netgroups will be protected\n    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed);\n    vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Protect the 8 nodes with the lowest minimum ping time.\n    // An attacker cannot manipulate this metric without physically moving nodes closer to the target.\n    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);\n    vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Protect 4 nodes that most recently sent us transactions.\n    // An attacker cannot manipulate this metric without performing useful work.\n    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime);\n    vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Protect 4 nodes that most recently sent us blocks.\n    // An attacker cannot manipulate this metric without performing useful work.\n    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime);\n    vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Protect the half of the remaining nodes which have been connected the longest.\n    // This replicates the non-eviction implicit behavior, and precludes attacks that start later.\n    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);\n    vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());\n\n    if (vEvictionCandidates.empty()) return false;\n\n    // Identify the network group with the most connections and youngest member.\n    // (vEvictionCandidates is already sorted by reverse connect time)\n    uint64_t naMostConnections;\n    unsigned int nMostConnections = 0;\n    int64_t nMostConnectionsTime = 0;\n    std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes;\n    BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) {\n        mapNetGroupNodes[node.nKeyedNetGroup].push_back(node);\n        int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected;\n        size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size();\n\n        if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {\n            nMostConnections = groupsize;\n            nMostConnectionsTime = grouptime;\n            naMostConnections = node.nKeyedNetGroup;\n        }\n    }\n\n    // Reduce to the network group with the most connections\n    vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]);\n\n    // Disconnect from the network group with the most connections\n    NodeId evicted = vEvictionCandidates.front().id;\n    LOCK(cs_vNodes);\n    for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) {\n        if ((*it)->GetId() == evicted) {\n            (*it)->fDisconnect = true;\n            return true;\n        }\n    }\n    return false;\n}\n\n// Randomly selects the destination CNode from a list of destination nodes\n// that have minimal number of routs from "mDandelionRoutes" going to them.\nCNode* CNode::SelectFromDandelionDestinations()\n{\n    std::map<CNode*,uint64_t> mDandelionDestinationCounts;\n    for (size_t i = 0; i < vDandelionDestination.size(); i++) {\n        mDandelionDestinationCounts.insert(std::make_pair(vDandelionDestination.at(i),0));\n    }\n    for (auto const& f : mDandelionRoutes) {\n        if (mDandelionDestinationCounts.find(f.second) != mDandelionDestinationCounts.end())\n            mDandelionDestinationCounts[f.second]++;\n    }\n    unsigned int minNumConnections = vDandelionInbound.size();\n    for (auto const& e : mDandelionDestinationCounts) {\n        if (e.second < minNumConnections) {\n            minNumConnections = e.second;\n        }\n    }\n    std::vector<CNode*> candidateDestinations;\n    for (auto const& e : mDandelionDestinationCounts) {\n        if (e.second == minNumConnections) {\n            candidateDestinations.push_back(e.first);\n        }\n    }\n    FastRandomContext rng;\n    CNode* dandelionDestination = nullptr;\n    if (candidateDestinations.size() > 0) {\n        dandelionDestination = candidateDestinations.at(rng.rand32() % candidateDestinations.size());\n    }\n    return dandelionDestination;\n}\n\nstd::string CNode::GetDandelionRoutingDataDebugString() {\n    std::string dandelionRoutingDataDebugString = "";\n    dandelionRoutingDataDebugString.append("  vDandelionInbound: ");\n    for(auto const& e : vDandelionInbound) {\n        dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" ");\n    }\n    dandelionRoutingDataDebugString.append("\\n");\n    dandelionRoutingDataDebugString.append("  vDandelionOutbound: ");\n    for(auto const& e : vDandelionOutbound) {\n        dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" ");\n    }\n    dandelionRoutingDataDebugString.append("\\n");\n    dandelionRoutingDataDebugString.append("  vDandelionDestination: ");\n    for(auto const& e : vDandelionDestination) {\n        dandelionRoutingDataDebugString.append(std::to_string(e->GetId())+" ");\n    }\n    dandelionRoutingDataDebugString.append("\\n");\n    dandelionRoutingDataDebugString.append("  mDandelionRoutes: ");\n    for(auto const& e : mDandelionRoutes) {\n        dandelionRoutingDataDebugString.append("("+std::to_string(e.first->GetId())+","+std::to_string(e.second->GetId())+") ");\n    }\n    dandelionRoutingDataDebugString.append("\\n");\n    dandelionRoutingDataDebugString.append("  localDandelionDestination: ");\n    if(localDandelionDestination == nullptr) {\n        dandelionRoutingDataDebugString.append("nullptr");\n    } else {\n        dandelionRoutingDataDebugString.append(std::to_string(localDandelionDestination->GetId()));\n    }\n    dandelionRoutingDataDebugString.append("\\n");\n    return dandelionRoutingDataDebugString;\n}\n\nvoid CConnman::AcceptConnection(const ListenSocket& hListenSocket) {\n    struct sockaddr_storage sockaddr;\n    socklen_t len = sizeof(sockaddr);\n    SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr *) &sockaddr, &len);\n    CAddress addr;\n    int nInbound = 0;\n    int nVerifiedInboundMasternodes = 0;\n    int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);\n\n    if (hSocket != INVALID_SOCKET)\n        if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))\n            LogPrintf("Warning: Unknown socket family\\n");\n\n    bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);\n    {\n        LOCK(cs_vNodes);\n        BOOST_FOREACH(CNode* pnode, vNodes) {\n            if (pnode->fInbound) {\n                nInbound++;\n                if (!pnode->verifiedProRegTxHash.IsNull()) {\n                    nVerifiedInboundMasternodes++;\n                }\n            }\n        }\n\n    }\n\n    if (hSocket == INVALID_SOCKET)\n    {\n        int nErr = WSAGetLastError();\n        if (nErr != WSAEWOULDBLOCK)\n            LogPrintf("socket error accept failed: %s\\n", NetworkErrorString(nErr));\n        return;\n    }\n\n    if (!fNetworkActive) {\n        LogPrintf("connection from %s dropped: not accepting new connections\\n", addr.ToString());\n        CloseSocket(hSocket);\n        return;\n    }\n\n    if (!IsSelectableSocket(hSocket))\n    {\n        LogPrintf("connection from %s dropped: non-selectable socket\\n", addr.ToString());\n        CloseSocket(hSocket);\n        return;\n    }\n\n    // According to the internet TCP_NODELAY is not carried into accepted sockets\n    // on all platforms.  Set it again here just to be sure.\n    int set = 1;\n#ifdef WIN32\n    setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));\n#else\n    setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));\n#endif\n\n    if (IsBanned(addr) && !whitelisted)\n    {\n        LogPrintf("connection from %s dropped (banned)\\n", addr.ToString());\n        CloseSocket(hSocket);\n        return;\n    }\n\n    if (nInbound - nVerifiedInboundMasternodes >= nMaxInbound)\n    {\n        if (!AttemptToEvictConnection()) {\n            // No connection to evict, disconnect the new connection\n            LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\\n");\n            CloseSocket(hSocket);\n            return;\n        }\n    }\n\n    NodeId id = GetNewNodeId();\n    uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();\n\n    CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, "", true);\n    pnode->AddRef();\n    pnode->fWhitelisted = whitelisted;\n    GetNodeSignals().InitializeNode(pnode, *this);\n\n    LogPrint("net", "connection from %s accepted\\n", addr.ToString());\n\n    {\n        LOCK(cs_vNodes);\n        vNodes.push_back(pnode);\n        // Dandelion: new inbound connection\n        CNode::vDandelionInbound.push_back(pnode);\n        CNode* pto = CNode::SelectFromDandelionDestinations();\n        if (pto != nullptr) {\n            CNode::mDandelionRoutes.insert(std::make_pair(pnode, pto));\n        }\n    }\n}\n\nvoid CNode::CloseDandelionConnections(const CNode* const pnode)\n{\n    // Remove pnode from vDandelionInbound, if present\n    for (auto iter=vDandelionInbound.begin(); iter!=vDandelionInbound.end();) {\n        if (*iter==pnode) {\n            iter=vDandelionInbound.erase(iter);\n        } else {\n            iter++;\n        }\n    }\n    // Remove pnode from vDandelionOutbound, if present\n    for (auto iter=vDandelionOutbound.begin(); iter!=vDandelionOutbound.end();) {\n        if (*iter==pnode) {\n            iter=vDandelionOutbound.erase(iter);\n        } else {\n            iter++;\n        }\n    }\n    // Remove pnode from vDandelionDestination, if present\n    bool isDandelionDestination = false;\n    for (auto iter = vDandelionDestination.begin(); iter != vDandelionDestination.end();) {\n        if (*iter == pnode) {\n            isDandelionDestination = true;\n            iter=vDandelionDestination.erase(iter);\n        } else {\n            iter++;\n        }\n    }\n    // Generate a replacement Dandelion destination, if necessary\n    if (isDandelionDestination) {\n        // Gather a vector of candidate replacements (outbound peers that are not already destinations)\n        std::vector<CNode*> candidateReplacements;\n        for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) {\n            bool eligibleCandidate = true;\n            for (auto iterj=vDandelionDestination.begin(); iterj!=vDandelionDestination.end();) {\n                if (*iteri==*iterj) {\n                    eligibleCandidate = false;\n                    iterj = vDandelionDestination.end();\n                } else {\n                    iterj++;\n                }\n            }\n            if (eligibleCandidate) {\n                candidateReplacements.push_back(*iteri);\n            }\n            iteri++;\n        }\n        // Select a candidate to be the replacement destination\n        FastRandomContext rng;\n        CNode* replacementDestination = nullptr;\n        if (candidateReplacements.size() > 0) {\n            replacementDestination = candidateReplacements.at(rng.rand32() % candidateReplacements.size());\n        }\n        if (replacementDestination != nullptr) {\n            vDandelionDestination.push_back(replacementDestination);\n        }\n    }\n    // Generate a replacement pnode, to be used if necessary\n    CNode* newPto = CNode::SelectFromDandelionDestinations();\n    // Remove from mDandelionRoutes, if present; if destination, try to replace\n    for(auto iter=mDandelionRoutes.begin(); iter!=mDandelionRoutes.end();) {\n        if (iter->first==pnode) {\n            iter = mDandelionRoutes.erase(iter);\n        } else if (iter->second==pnode) {\n            if (newPto==nullptr) {\n                iter = mDandelionRoutes.erase(iter);\n            } else {\n                iter->second = newPto;\n                iter++;\n            }\n        } else {\n            iter++;\n        }\n    }\n    // Replace localDandelionDestination if equal to pnode\n    if (localDandelionDestination == pnode) {\n        localDandelionDestination = newPto;\n    }\n}\n\nvoid ThreadMessageHandler()\n{\n    SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);\n    while (true)\n    {\n        {\n            LOCK(cs_vNodes);\n            // Disconnect unused nodes\n            std::vector<CNode*> vNodesCopy = vNodes;\n            BOOST_FOREACH(CNode* pnode, vNodesCopy)\n            {\n                if (pnode->fDisconnect)\n                {\n                    // remove from vNodes\n                    vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());\n\n                    // release outbound grant (if any)\n                    pnode->grantOutbound.Release();\n                    pnode->grantMasternodeOutbound.Release();\n\n                    // close socket and cleanup\n                    pnode->CloseSocketDisconnect();\n\n                    // hold in disconnected pool until all refs are released\n                    pnode->Release();\n                    vNodesDisconnected.push_back(pnode);\n                }\n            }\n        }\n        {\n            // Delete disconnected nodes\n            std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;\n            BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy)\n            {\n                // wait until threads are done using it\n                if (pnode->GetRefCount() <= 0) {\n                    bool fDelete = false;\n                    {\n                        TRY_LOCK(pnode->cs_inventory, lockInv);\n                        if (lockInv) {\n                            TRY_LOCK(pnode->cs_vSend, lockSend);\n                            if (lockSend) {\n                                fDelete = true;\n                            }\n                        }\n                    }\n                    if (fDelete) {\n                        // Dandelion: close connection\n                        CNode::CloseDandelionConnections(pnode);\n                        vNodesDisconnected.remove(pnode);\n                        DeleteNode(pnode);\n                    }\n                }\n            }\n        }\n        size_t vNodesSize;\n        {\n            LOCK(cs_vNodes);\n        }\n\n        if (fSleep)\n            MilliSleep(100);\n    }\n}\n\n\n        //\n        // Find which sockets have data to receive\n        //\n        struct timeval timeout;\n        timeout.tv_sec  = 0;\n        timeout.tv_usec = 50000; // frequency to poll pnode->vSend\n\n        fd_set fdsetRecv;\n        fd_set fdsetSend;\n        fd_set fdsetError;\n        FD_ZERO(&fdsetRecv);\n        FD_ZERO(&fdsetSend);\n        FD_ZERO(&fdsetError);\n        SOCKET hSocketMax = 0;\n        bool have_fds = false;\n\n        BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {\n            FD_SET(hListenSocket.socket, &fdsetRecv);\n            hSocketMax = std::max(hSocketMax, hListenSocket.socket);\n            have_fds = true;\n        }\n\n        {\n            LOCK(cs_vNodes);\n            BOOST_FOREACH(CNode* pnode, vNodes)\n            {\n                // Implement the following logic:\n                // * If there is data to send, select() for sending data. As this only\n                //   happens when optimistic write failed, we choose to first drain the\n                //   write buffer in this case before receiving more. This avoids\n                //   needlessly queueing received data, if the remote peer is not themselves\n                //   receiving data. This means properly utilizing TCP flow control signalling.\n                // * Otherwise, if there is space left in the receive buffer, select() for\n                //   receiving data.\n                // * Hand off all complete messages to the processor, to be handled without\n                //   blocking here.\n\n                bool select_recv = !pnode->fPauseRecv;\n                bool select_send;\n                {\n                    LOCK(pnode->cs_vSend);\n                    select_send = !pnode->vSendMsg.empty();\n                }\n\n                LOCK(pnode->cs_hSocket);\n                if (pnode->hSocket == INVALID_SOCKET)\n                    continue;\n\n                FD_SET(pnode->hSocket, &fdsetError);\n                hSocketMax = std::max(hSocketMax, pnode->hSocket);\n                have_fds = true;\n\n                if (select_send) {\n                    FD_SET(pnode->hSocket, &fdsetSend);\n                    continue;\n                }\n                if (select_recv) {\n                    FD_SET(pnode->hSocket, &fdsetRecv);\n                }\n            }\n        }\n\n        int nSelect = select(have_fds ? hSocketMax + 1 : 0,\n                             &fdsetRecv, &fdsetSend, &fdsetError, &timeout);\n        if (interruptNet)\n            return;\n\n        if (nSelect == SOCKET_ERROR)\n        {\n            if (have_fds)\n            {\n                int nErr = WSAGetLastError();\n                LogPrintf("socket select error %s\\n", NetworkErrorString(nErr));\n                for (unsigned int i = 0; i <= hSocketMax; i++)\n                    FD_SET(i, &fdsetRecv);\n            }\n            FD_ZERO(&fdsetSend);\n            FD_ZERO(&fdsetError);\n            if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))\n                return;\n        }\n\n        //\n        // Accept new connections\n        //\n        BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)\n        {\n            if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))\n            {\n                AcceptConnection(hListenSocket);\n            }\n        }\n\n        //\n        // Service each socket\n        //\n        std::vector<CNode*> vNodesCopy;\n        {\n            LOCK(cs_vNodes);\n            vNodesCopy = vNodes;\n            BOOST_FOREACH(CNode* pnode, vNodesCopy)\n                pnode->AddRef();\n        }\n        BOOST_FOREACH(CNode* pnode, vNodesCopy)\n        {\n            if (interruptNet)\n                return;\n\n            //\n            // Receive\n            //\n            bool recvSet = false;\n            bool sendSet = false;\n            bool errorSet = false;\n            {\n                LOCK(pnode->cs_hSocket);\n                if (pnode->hSocket == INVALID_SOCKET)\n                    continue;\n                recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);\n                sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);\n                errorSet = FD_ISSET(pnode->hSocket, &fdsetError);\n            }\n            if (recvSet || errorSet)\n            {\n                {\n                    {\n                        // typical socket buffer is 8K-64K\n                        char pchBuf[0x10000];\n                        int nBytes = 0;\n                        {\n                            LOCK(pnode->cs_hSocket);\n                            if (pnode->hSocket == INVALID_SOCKET)\n                                continue;\n                            nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);\n                        }\n                        if (nBytes > 0)\n                        {\n                            bool notify = false;\n                            if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))\n                                pnode->CloseSocketDisconnect();\n                            RecordBytesRecv(nBytes);\n                            if (notify) {\n                                size_t nSizeAdded = 0;\n                                auto it(pnode->vRecvMsg.begin());\n                                for (; it != pnode->vRecvMsg.end(); ++it) {\n                                    if (!it->complete())\n                                        break;\n                                    nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;\n                                }\n                                {\n                                    LOCK(pnode->cs_vProcessMsg);\n                                    pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);\n                                    pnode->nProcessQueueSize += nSizeAdded;\n                                    pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;\n                                }\n                                WakeMessageHandler();\n                            }\n                        }\n                        else if (nBytes == 0)\n                        {\n                            // socket closed gracefully\n                            if (!pnode->fDisconnect)\n                                LogPrint("net", "socket closed\\n");\n                            pnode->CloseSocketDisconnect();\n                        }\n                        else if (nBytes < 0)\n                        {\n                            // error\n                            int nErr = WSAGetLastError();\n                            if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)\n                            {\n                                if (!pnode->fDisconnect)\n                                    LogPrintf("socket recv error %s\\n", NetworkErrorString(nErr));\n                                pnode->CloseSocketDisconnect();\n                            }\n                        }\n                    }\n                }\n            }\n\n            //\n            // Send\n            //\n            if (sendSet)\n            {\n                LOCK(pnode->cs_vSend);\n                size_t nBytes = SocketSendData(pnode);\n                if (nBytes) {\n                    RecordBytesSent(nBytes);\n                }\n            }\n\n            //\n            // Inactivity checking\n            //\n            int64_t nTime = GetSystemTimeInSeconds();\n            if (nTime - pnode->nTimeConnected > 60)\n            {\n                if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)\n                {\n                    LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);\n                    pnode->fDisconnect = true;\n                }\n                else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)\n                {\n                    LogPrintf("socket sending timeout: %is\\n", nTime - pnode->nLastSend);\n                    pnode->fDisconnect = true;\n                }\n                else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))\n                {\n                    LogPrintf("socket receive timeout: %is\\n", nTime - pnode->nLastRecv);\n                    pnode->fDisconnect = true;\n                }\n                else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())\n                {\n                    LogPrintf("ping timeout: %fs\\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));\n                    pnode->fDisconnect = true;\n                }\n                else if (!pnode->fSuccessfullyConnected)\n                {\n                    LogPrintf("version handshake timeout from %d\\n", pnode->id);\n                    pnode->fDisconnect = true;\n                }\n            }\n        }\n        {\n            LOCK(cs_vNodes);\n            BOOST_FOREACH(CNode* pnode, vNodesCopy)\n                pnode->Release();\n        }\n    }\n}\n\nvoid CConnman::WakeMessageHandler()\n{\n    {\n        std::lock_guard<std::mutex> lock(mutexMsgProc);\n        fMsgProcWake = true;\n    }\n    condMsgProc.notify_one();\n}\n\n\n\n\n\n\n#ifdef USE_UPNP\nvoid ThreadMapPort()\n{\n    std::string port = strprintf("%u", GetListenPort());\n    const char * multicastif = 0;\n    const char * minissdpdpath = 0;\n    struct UPNPDev * devlist = 0;\n    char lanaddr[64];\n\n#ifndef UPNPDISCOVER_SUCCESS\n    /* miniupnpc 1.5 */\n    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);\n#elif MINIUPNPC_API_VERSION < 14\n    /* miniupnpc 1.6 */\n    int error = 0;\n    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);\n#else\n    /* miniupnpc 1.9.20150730 */\n    int error = 0;\n    devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);\n#endif\n\n    struct UPNPUrls urls;\n    struct IGDdatas data;\n    int r;\n\n    r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));\n    if (r == 1)\n    {\n        if (fDiscover) {\n            char externalIPAddress[40];\n            r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);\n            if(r != UPNPCOMMAND_SUCCESS)\n                LogPrintf("UPnP: GetExternalIPAddress() returned %d\\n", r);\n            else\n            {\n                if(externalIPAddress[0])\n                {\n                    CNetAddr resolved;\n                    if(LookupHost(externalIPAddress, resolved, false)) {\n                        LogPrintf("UPnP: ExternalIPAddress = %s\\n", resolved.ToString().c_str());\n                        AddLocal(resolved, LOCAL_UPNP);\n                    }\n                }\n                else\n                    LogPrintf("UPnP: GetExternalIPAddress failed.\\n");\n            }\n        }\n\n        std::string strDesc = "Bitcoin " + FormatFullVersion();\n\n        try {\n            while (true) {\n#ifndef UPNPDISCOVER_SUCCESS\n                /* miniupnpc 1.5 */\n                r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,\n                                    port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);\n#else\n                /* miniupnpc 1.6 */\n                r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,\n                                    port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");\n#endif\n\n                if(r!=UPNPCOMMAND_SUCCESS)\n                    LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\\n",\n                        port, port, lanaddr, r, strupnperror(r));\n                else\n                    LogPrintf("UPnP Port Mapping successful.\\n");\n\n                MilliSleep(20*60*1000); // Refresh every 20 minutes\n            }\n        }\n        catch (const boost::thread_interrupted&)\n        {\n            r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);\n            LogPrintf("UPNP_DeletePortMapping() returned: %d\\n", r);\n            freeUPNPDevlist(devlist); devlist = 0;\n            FreeUPNPUrls(&urls);\n            throw;\n        }\n    } else {\n        LogPrintf("No valid UPnP IGDs found\\n");\n        freeUPNPDevlist(devlist); devlist = 0;\n        if (r != 0)\n            FreeUPNPUrls(&urls);\n    }\n}\n\nvoid MapPort(bool fUseUPnP)\n{\n    static boost::thread* upnp_thread = NULL;\n\n    if (fUseUPnP)\n    {\n        if (upnp_thread) {\n            upnp_thread->interrupt();\n            upnp_thread->join();\n            delete upnp_thread;\n        }\n        upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort));\n    }\n    else if (upnp_thread) {\n        upnp_thread->interrupt();\n        upnp_thread->join();\n        delete upnp_thread;\n        upnp_thread = NULL;\n    }\n}\n\n#else\nvoid MapPort(bool)\n{\n    // Intentionally left blank.\n}\n#endif\n\n\n\n\n\n\nstatic std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits)\n{\n    //use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK)\n    if (!data.supportsServiceBitsFiltering || *requiredServiceBits == NODE_NETWORK) {\n        *requiredServiceBits = NODE_NETWORK;\n        return data.host;\n    }\n\n    // See chainparams.cpp, most dnsseeds only support one or two possible servicebits hostnames\n    return strprintf("x%x.%s", *requiredServiceBits, data.host);\n}\n\n\nvoid CConnman::ThreadDNSAddressSeed()\n{\n    // goal: only query DNS seeds if address need is acute\n    // Avoiding DNS seeds when we don\'t need them improves user privacy by\n    //  creating fewer identifying DNS requests, reduces trust by giving seeds\n    //  less influence on the network topology, and reduces traffic to the seeds.\n    if ((addrman.size() > 0) &&\n        (!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {\n        if (!interruptNet.sleep_for(std::chrono::seconds(11)))\n            return;\n\n        LOCK(cs_vNodes);\n        int nRelevant = 0;\n        for (auto pnode : vNodes) {\n            nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices);\n        }\n        if (nRelevant >= 2) {\n            LogPrintf("P2P peers available. Skipped DNS seeding.\\n");\n            return;\n        }\n    }\n\n    const std::vector<CDNSSeedData> &vSeeds = Params().DNSSeeds();\n    int found = 0;\n\n    LogPrintf("Loading addresses from DNS seeds (could take a while)\\n");\n\n    BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) {\n        if (interruptNet) {\n            return;\n        }\n        if (HaveNameProxy()) {\n            AddOneShot(seed.host);\n        } else {\n            std::vector<CNetAddr> vIPs;\n            std::vector<CAddress> vAdd;\n            ServiceFlags requiredServiceBits = nRelevantServices;\n            if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true))\n            {\n                BOOST_FOREACH(const CNetAddr& ip, vIPs)\n                {\n                    int nOneDay = 24*3600;\n                    CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits);\n                    addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old\n                    vAdd.push_back(addr);\n                    found++;\n                }\n            }\n            if (interruptNet) {\n                return;\n            }\n            // TODO: The seed name resolve may fail, yielding an IP of [::], which results in\n            // addrman assigning the same source to results from different seeds.\n            // This should switch to a hard-coded stable dummy IP for each seed name, so that the\n            // resolve is not required at all.\n            if (!vIPs.empty()) {\n                CService seedSource;\n                Lookup(seed.name.c_str(), seedSource, 0, true);\n                addrman.Add(vAdd, seedSource);\n            }\n        }\n    }\n\n    LogPrintf("%d addresses found from DNS seeds\\n", found);\n}\n\n\n\n\n\n\n\n\n\n\n\n\nvoid CConnman::DumpAddresses()\n{\n    int64_t nStart = GetTimeMillis();\n\n    CAddrDB adb;\n    adb.Write(addrman);\n\n    LogPrint("net", "Flushed %d addresses to peers.dat  %dms\\n",\n           addrman.size(), GetTimeMillis() - nStart);\n}\n\nvoid CConnman::DumpData()\n{\n    DumpAddresses();\n    DumpBanlist();\n}\n\nvoid CConnman::ProcessOneShot()\n{\n    std::string strDest;\n    {\n        LOCK(cs_vOneShots);\n        if (vOneShots.empty())\n            return;\n        strDest = vOneShots.front();\n        vOneShots.pop_front();\n    }\n    CAddress addr;\n    CSemaphoreGrant grant(*semOutbound, true);\n    if (grant) {\n        if (!OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true))\n            AddOneShot(strDest);\n    }\n}\n\nvoid CConnman::ThreadOpenConnections()\n{\n    // Connect to specific addresses\n    if (mapMultiArgs.count("-connect") && mapMultiArgs.at("-connect").size() > 0)\n    {\n        for (int64_t nLoop = 0;; nLoop++)\n        {\n            ProcessOneShot();\n            BOOST_FOREACH(const std::string& strAddr, mapMultiArgs.at("-connect"))\n            {\n                CAddress addr(CService(), NODE_NONE);\n                OpenNetworkConnection(addr, false, NULL, strAddr.c_str());\n                for (int i = 0; i < 10 && i < nLoop; i++)\n                {\n                    if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))\n                        return;\n                }\n            }\n            if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))\n                return;\n        }\n    }\n\n    // Initiate network connections\n    int64_t nStart = GetTime();\n\n    // Minimum time before next feeler connection (in microseconds).\n    int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);\n    while (!interruptNet)\n    {\n        ProcessOneShot();\n\n        if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))\n            return;\n\n        CSemaphoreGrant grant(*semOutbound);\n        if (interruptNet)\n            return;\n\n        // Add seed nodes if DNS seeds are all down (an infrastructure attack?).\n        if (addrman.size() == 0 && (GetTime() - nStart > 60)) {\n            static bool done = false;\n            if (!done) {\n                LogPrintf("Adding fixed seed nodes as DNS doesn\'t seem to be available.\\n");\n                CNetAddr local;\n                LookupHost("127.0.0.1", local, false);\n                addrman.Add(convertSeed6(Params().FixedSeeds()), local);\n                done = true;\n            }\n        }\n\n        //\n        // Choose an address to connect to based on most recently seen\n        //\n        CAddress addrConnect;\n\n        // Only connect out to one peer per network group (/16 for IPv4).\n        // Do this here so we don\'t have to critsect vNodes inside mapAddresses critsect.\n        int nOutbound = 0;\n        int nOutboundRelevant = 0;\n        std::set<std::vector<unsigned char> > setConnected;\n        {\n            LOCK(cs_vNodes);\n            BOOST_FOREACH(CNode* pnode, vNodes) {\n                if (!pnode->fInbound && !pnode->fAddnode) {\n\n                    // Count the peers that have all relevant services\n                    if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) {\n                        nOutboundRelevant++;\n                    }\n                    // Netgroups for inbound and addnode peers are not excluded because our goal here\n                    // is to not use multiple of our limited outbound slots on a single netgroup\n                    // but inbound and addnode peers do not use our outbound slots.  Inbound peers\n                    // also have the added issue that they\'re attacker controlled and could be used\n                    // to prevent us from connecting to particular hosts if we used them here.\n                    setConnected.insert(pnode->addr.GetGroup());\n                    nOutbound++;\n                }\n            }\n        }\n\n        // Feeler Connections\n        //\n        // Design goals:\n        //  * Increase the number of connectable addresses in the tried table.\n        //\n        // Method:\n        //  * Choose a random address from new and attempt to connect to it if we can connect \n        //    successfully it is added to tried.\n        //  * Start attempting feeler connections only after node finishes making outbound \n        //    connections.\n        //  * Only make a feeler connection once every few minutes.\n        //\n        bool fFeeler = false;\n        if (nOutbound >= nMaxOutbound) {\n            int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).\n            if (nTime > nNextFeeler) {\n                nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);\n                fFeeler = true;\n            } else {\n                continue;\n            }\n        }\n\n        int64_t nANow = GetAdjustedTime();\n        int nTries = 0;\n        while (!interruptNet)\n        {\n            CAddrInfo addr = addrman.Select(fFeeler);\n\n            // if we selected an invalid address, restart\n            if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))\n                break;\n\n            // If we didn\'t find an appropriate destination after trying 100 addresses fetched from addrman,\n            // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates\n            // already-connected network ranges, ...) before trying new addrman addresses.\n            nTries++;\n            if (nTries > 100)\n                break;\n\n            if (IsLimited(addr))\n                continue;\n\n            // only connect to full nodes\n            if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)\n                continue;\n\n            // only consider very recently tried nodes after 30 failed attempts\n            if (nANow - addr.nLastTry < 600 && nTries < 30)\n                continue;\n\n            // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up.\n            ServiceFlags nRequiredServices = nRelevantServices;\n            if (nTries >= 40 && nOutbound < (nMaxOutbound >> 1)) {\n                nRequiredServices = REQUIRED_SERVICES;\n            }\n\n            if ((addr.nServices & nRequiredServices) != nRequiredServices) {\n                continue;\n            }\n\n            // do not allow non-default ports, unless after 50 invalid addresses selected already\n            if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)\n                continue;\n\n            addrConnect = addr;\n\n            // regardless of the services assumed to be available, only require the minimum if half or more outbound have relevant services\n            if (nOutboundRelevant >= (nMaxOutbound >> 1)) {\n                addrConnect.nServices = REQUIRED_SERVICES;\n            } else {\n                addrConnect.nServices = nRequiredServices;\n            }\n            break;\n        }\n\n        if (addrConnect.IsValid()) {\n\n            if (fFeeler) {\n                // Add small amount of random noise before connection to avoid synchronization.\n                int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);\n                if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep)))\n                    return;\n                LogPrint("net", "Making feeler connection to %s\\n", addrConnect.ToString());\n            }\n\n            OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler);\n        }\n    }\n}\n\nstd::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()\n{\n    std::vector<AddedNodeInfo> ret;\n\n    std::list<std::string> lAddresses(0);\n    {\n        LOCK(cs_vAddedNodes);\n        ret.reserve(vAddedNodes.size());\n        BOOST_FOREACH(const std::string& strAddNode, vAddedNodes)\n            lAddresses.push_back(strAddNode);\n    }\n\n\n    // Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService\n    std::map<CService, bool> mapConnected;\n    std::map<std::string, std::pair<bool, CService>> mapConnectedByName;\n    {\n        LOCK(cs_vNodes);\n        for (const CNode* pnode : vNodes) {\n            if (pnode->addr.IsValid()) {\n                mapConnected[pnode->addr] = pnode->fInbound;\n            }\n            std::string addrName = pnode->GetAddrName();\n            if (!addrName.empty()) {\n                mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr));\n            }\n        }\n    }\n\n    BOOST_FOREACH(const std::string& strAddNode, lAddresses) {\n        CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort()));\n        if (service.IsValid()) {\n            // strAddNode is an IP:port\n            auto it = mapConnected.find(service);\n            if (it != mapConnected.end()) {\n                ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second});\n            } else {\n                ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false});\n            }\n        } else {\n            // strAddNode is a name\n            auto it = mapConnectedByName.find(strAddNode);\n            if (it != mapConnectedByName.end()) {\n                ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first});\n            } else {\n                ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false});\n            }\n        }\n    }\n\n    return ret;\n}\n\nvoid CConnman::ThreadOpenAddedConnections()\n{\n    {\n        LOCK(cs_vAddedNodes);\n        if (mapMultiArgs.count("-addnode"))\n            vAddedNodes = mapMultiArgs.at("-addnode");\n    }\n\n    while (true)\n    {\n        CSemaphoreGrant grant(*semAddnode);\n        std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();\n        bool tried = false;\n        for (const AddedNodeInfo& info : vInfo) {\n            if (!info.fConnected) {\n                if (!grant.TryAcquire()) {\n                    // If we\'ve used up our semaphore and need a new one, lets not wait here since while we are waiting\n                    // the addednodeinfo state might change.\n                    break;\n                }\n                // If strAddedNode is an IP/port, decode it immediately, so\n                // OpenNetworkConnection can detect existing connections to that IP/port.\n                tried = true;\n                CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));\n                OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true);\n                if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))\n                    return;\n            }\n        }\n        // Retry every 60 seconds if a connection was attempted, otherwise two seconds\n        if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))\n            return;\n    }\n}\n\nvoid CConnman::ThreadOpenMasternodeConnections()\n{\n    // Connecting to specific addresses, no masternode connections available\n    if (IsArgSet("-connect") && mapMultiArgs.at("-connect").size() > 0)\n        return;\n\n    while (!interruptNet)\n    {\n        if (!interruptNet.sleep_for(std::chrono::milliseconds(1000)))\n            return;\n\n        std::set<CService> connectedNodes;\n        std::set<uint256> connectedProRegTxHashes;\n        ForEachNode([&](const CNode* pnode) {\n            connectedNodes.emplace(pnode->addr);\n            if (!pnode->verifiedProRegTxHash.IsNull()) {\n                connectedProRegTxHashes.emplace(pnode->verifiedProRegTxHash);\n            }\n        });\n\n        auto mnList = deterministicMNManager->GetListAtChainTip();\n\n        CSemaphoreGrant grant(*semMasternodeOutbound);\n        if (interruptNet)\n            return;\n\n        int64_t nANow = GetAdjustedTime();\n\n        // NOTE: Process only one pending masternode at a time\n\n        CService addr;\n        { // don\'t hold lock while calling OpenMasternodeConnection as cs_main is locked deep inside\n            LOCK2(cs_vNodes, cs_vPendingMasternodes);\n\n            std::vector<CService> pending;\n            for (const auto& group : masternodeQuorumNodes) {\n                for (const auto& proRegTxHash : group.second) {\n                    auto dmn = mnList.GetMN(proRegTxHash);\n                    if (!dmn) {\n                        continue;\n                    }\n                    const auto& addr2 = dmn->pdmnState->addr;\n                    if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {\n                        auto addrInfo = addrman.GetAddressInfo(addr2);\n                        // back off trying connecting to an address if we already tried recently\n                        if (addrInfo.IsValid() && nANow - addrInfo.nLastTry < 60) {\n                            continue;\n                        }\n                        pending.emplace_back(addr2);\n                    }\n                }\n            }\n\n            if (!vPendingMasternodes.empty()) {\n                auto addr2 = vPendingMasternodes.front();\n                vPendingMasternodes.erase(vPendingMasternodes.begin());\n                if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2)) {\n                    pending.emplace_back(addr2);\n                }\n            }\n\n            if (pending.empty()) {\n                // nothing to do, keep waiting\n                continue;\n            }\n\n            Shuffle(pending.begin(), pending.end(), FastRandomContext());\n            addr = pending.front();\n        }\n\n        OpenMasternodeConnection(CAddress(addr, NODE_NETWORK));\n        // should be in the list now if connection was opened\n        ForNode(addr, CConnman::AllNodes, [&](CNode* pnode) {\n            if (pnode->fDisconnect) {\n                return false;\n            }\n            grant.MoveTo(pnode->grantMasternodeOutbound);\n            return true;\n        });\n    }\n}\n\n// if successful, this moves the passed grant to the constructed node\nbool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode, bool fConnectToMasternode)\n{\n    //\n    // Initiate outbound network connection\n    //\n    if (interruptNet) {\n        return false;\n    }\n    if (!fNetworkActive) {\n        return false;\n    }\n    bool fAllowLocal = fMasternodeMode;\n    if (!pszDest) {\n        // banned or exact match?\n        if (IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort()))\n            return false;\n        // local and not a connection to itself?\n        if (!fAllowLocal && IsLocal(addrConnect))\n            return false;\n    } else if (FindNode(std::string(pszDest)))\n        return false;\n\n    CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, fMasternodeMode);\n\n    if (!pnode)\n        return false;\n    if (grantOutbound)\n        grantOutbound->MoveTo(pnode->grantOutbound);\n    if (fOneShot)\n        pnode->fOneShot = true;\n    if (fFeeler)\n        pnode->fFeeler = true;\n    if (fConnectToMasternode)\n        pnode->fZnode = true;\n\n    // Martun: if dandelion is enabled, then send a special transaction\n    // to the new peer to check, if the peer supports dandelion or not.\n    if (GetBoolArg("-dandelion", true)) {\n        LOCK(cs_vNodes);\n        // Dandelion: new outbound connection\n        CNode::vDandelionOutbound.push_back(pnode);\n        const Consensus::Params& consensus = Params().GetConsensus();\n        if (CNode::vDandelionDestination.size() < consensus.nDandelionMaxDestinations) {\n            CNode::vDandelionDestination.push_back(pnode);\n        }\n        // Dandelion service discovery\n        uint256 dummyHash;\n        dummyHash.SetHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");\n        CInv dummyInv(MSG_DANDELION_TX, dummyHash);\n        pnode->PushInventory(dummyInv);\n    }\n    \n    if (fAddnode)\n        pnode->fAddnode = true;\n\n    GetNodeSignals().InitializeNode(pnode, *this);\n    {\n        LOCK(cs_vNodes);\n        vNodes.push_back(pnode);\n    }\n\n    return true;\n}\n\nbool CConnman::OpenMasternodeConnection(const CAddress &addrConnect) {\n    return OpenNetworkConnection(addrConnect, false, NULL, NULL, false, false, false, true);\n}\n\nvoid CConnman::ThreadMessageHandler()\n{\n    while (!flagInterruptMsgProc)\n    {\n        std::vector<CNode*> vNodesCopy;\n        {\n            LOCK(cs_vNodes);\n            vNodesCopy = vNodes;\n            BOOST_FOREACH(CNode* pnode, vNodesCopy) {\n                pnode->AddRef();\n            }\n        }\n\n        bool fMoreWork = false;\n\n        BOOST_FOREACH(CNode* pnode, vNodesCopy)\n        {\n            if (pnode->fDisconnect)\n                continue;\n\n            // Receive messages\n            bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc);\n            fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);\n            if (flagInterruptMsgProc)\n                return;\n\n            // Send messages\n            {\n                LOCK(pnode->cs_sendProcessing);\n                GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc);\n            }\n            if (flagInterruptMsgProc)\n                return;\n        }\n\n        {\n            LOCK(cs_vNodes);\n            BOOST_FOREACH(CNode* pnode, vNodesCopy)\n                pnode->Release();\n        }\n\n        std::unique_lock<std::mutex> lock(mutexMsgProc);\n        if (!fMoreWork) {\n            condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });\n        }\n        fMsgProcWake = false;\n    }\n}\n\n\n\n\n\n\nbool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)\n{\n    strError = "";\n    int nOne = 1;\n\n    // Create socket for listening for incoming connections\n    struct sockaddr_storage sockaddr;\n    socklen_t len = sizeof(sockaddr);\n    if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))\n    {\n        strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString());\n        LogPrintf("%s\\n", strError);\n        return false;\n    }\n\n    SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);\n    if (hListenSocket == INVALID_SOCKET)\n    {\n        strError = strprintf("Error: Couldn\'t open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError()));\n        LogPrintf("%s\\n", strError);\n        return false;\n    }\n    if (!IsSelectableSocket(hListenSocket))\n    {\n        strError = "Error: Couldn\'t create a listenable socket for incoming connections";\n        LogPrintf("%s\\n", strError);\n        return false;\n    }\n\n\n#ifndef WIN32\n#ifdef SO_NOSIGPIPE\n    // Different way of disabling SIGPIPE on BSD\n    setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));\n#endif\n    // Allow binding if the port is still in TIME_WAIT state after\n    // the program was closed and restarted.\n    setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));\n    // Disable Nagle\'s algorithm\n    setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int));\n#else\n    setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int));\n    setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int));\n#endif\n\n    // Set to non-blocking, incoming connections will also inherit this\n    if (!SetSocketNonBlocking(hListenSocket, true)) {\n        strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\\n", NetworkErrorString(WSAGetLastError()));\n        LogPrintf("%s\\n", strError);\n        return false;\n    }\n\n    // some systems don\'t have IPV6_V6ONLY but are always v6only; others do have the option\n    // and enable it by default or not. Try to enable it, if possible.\n    if (addrBind.IsIPv6()) {\n#ifdef IPV6_V6ONLY\n#ifdef WIN32\n        setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));\n#else\n        setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));\n#endif\n#endif\n#ifdef WIN32\n        int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;\n        setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));\n#endif\n    }\n\n    if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)\n    {\n        int nErr = WSAGetLastError();\n        if (nErr == WSAEADDRINUSE)\n            strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), _(PACKAGE_NAME));\n        else\n            strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));\n        LogPrintf("%s\\n", strError);\n        CloseSocket(hListenSocket);\n        return false;\n    }\n    LogPrintf("Bound to %s\\n", addrBind.ToString());\n\n    // Listen for incoming connections\n    if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)\n    {\n        strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));\n        LogPrintf("%s\\n", strError);\n        CloseSocket(hListenSocket);\n        return false;\n    }\n\n    vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));\n\n    if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)\n        AddLocal(addrBind, LOCAL_BIND);\n\n    return true;\n}\n\nvoid Discover(boost::thread_group& threadGroup)\n{\n    if (!fDiscover)\n        return;\n\n#ifdef WIN32\n    // Get local host IP\n    char pszHostName[256] = "";\n    if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)\n    {\n        std::vector<CNetAddr> vaddr;\n        if (LookupHost(pszHostName, vaddr, 0, true))\n        {\n            BOOST_FOREACH (const CNetAddr &addr, vaddr)\n            {\n                if (AddLocal(addr, LOCAL_IF))\n                    LogPrintf("%s: %s - %s\\n", __func__, pszHostName, addr.ToString());\n            }\n        }\n    }\n#else\n    // Get local host ip\n    struct ifaddrs* myaddrs;\n    if (getifaddrs(&myaddrs) == 0)\n    {\n        for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)\n        {\n            if (ifa->ifa_addr == NULL) continue;\n            if ((ifa->ifa_flags & IFF_UP) == 0) continue;\n            if (strcmp(ifa->ifa_name, "lo") == 0) continue;\n            if (strcmp(ifa->ifa_name, "lo0") == 0) continue;\n            if (ifa->ifa_addr->sa_family == AF_INET)\n            {\n                struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);\n                CNetAddr addr(s4->sin_addr);\n                if (AddLocal(addr, LOCAL_IF))\n                    LogPrintf("%s: IPv4 %s: %s\\n", __func__, ifa->ifa_name, addr.ToString());\n            }\n            else if (ifa->ifa_addr->sa_family == AF_INET6)\n            {\n                struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);\n                CNetAddr addr(s6->sin6_addr);\n                if (AddLocal(addr, LOCAL_IF))\n                    LogPrintf("%s: IPv6 %s: %s\\n", __func__, ifa->ifa_name, addr.ToString());\n            }\n        }\n        freeifaddrs(myaddrs);\n    }\n#endif\n}\n\nvoid CNode::DandelionShuffle() {    \n    std::vector<CNode *> vNodes = g_connman->CopyNodeVector([] (const CNode *)->bool {return true;});\n    {\n        // Iterate through mDandelionRoutes to facilitate bookkeeping\n        mDandelionRoutes.clear();\n        // Set localDandelionDestination to nulltpr and perform bookkeeping\n        if (localDandelionDestination != nullptr) {\n            localDandelionDestination = nullptr;\n        }\n        // Clear vDandelionDestination\n        //  (bookkeeping already done while iterating through mDandelionRoutes)\n        vDandelionDestination.clear();\n        // Repopulate vDandelionDestination\n        const Consensus::Params& consensus = Params().GetConsensus();\n        if (vDandelionDestination.size() < consensus.nDandelionMaxDestinations &&\n            vDandelionDestination.size() < vDandelionOutbound.size()) {\n            std::vector<CNode*> candidateDestinations;\n            for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) {\n                bool eligibleCandidate = true;\n                for (auto iterj = vDandelionDestination.begin(); iterj != vDandelionDestination.end(); ++iterj) {\n                    if (*iteri==*iterj) {\n                        eligibleCandidate = false;\n                        break;\n                    }\n                }\n                if (eligibleCandidate) {\n                    candidateDestinations.push_back(*iteri);\n                }\n                ++iteri;\n            }\n            // Sample "vDandelionDestination.size() - DANDELION_MAX_DESTINATIONS" destinations\n            // if there are that many to choose from.\n            FastRandomContext rng;\n            const Consensus::Params& consensus = Params().GetConsensus();\n            while (vDandelionDestination.size() < consensus.nDandelionMaxDestinations &&\n                   vDandelionDestination.size() < vDandelionOutbound.size() &&\n                   candidateDestinations.size() > 0) {\n                int rand_index = rng.rand32() % candidateDestinations.size();\n                vDandelionDestination.push_back(candidateDestinations[rand_index]);\n                candidateDestinations.erase(candidateDestinations.begin() + rand_index);\n            }\n        }\n\n        // Generate new routes\n        for (auto pnode : vDandelionInbound) {\n            CNode* pto = CNode::SelectFromDandelionDestinations();\n            if (pto != nullptr) {\n                mDandelionRoutes.insert(std::make_pair(pnode, pto));\n            }\n        }\n        localDandelionDestination = CNode::SelectFromDandelionDestinations();\n    }\n    g_connman->ReleaseNodeVector(vNodes);\n}\n\nvoid CConnman::ThreadDandelionShuffle() {\n    int64_t nNextDandelionShuffle = 0;\n    while (!interruptNet) {\n        if (GetTimeMicros() > nNextDandelionShuffle) {\n            CNode::DandelionShuffle();\n            const Consensus::Params& consensus = Params().GetConsensus();\n            nNextDandelionShuffle = PoissonNextSend(\n                GetTimeMicros(), consensus.nDandelionShuffleInterval);\n            // Sleep for 1 second until the next shuffle time.\n            // Sleeping for DANDELION_SHUFFLE_INTERVAL seconds at once\n            // results to not being able to close firo.\n            int time_to_sleep = (nNextDandelionShuffle - GetTimeMicros()) / 1000;\n            while (time_to_sleep > 0) {\n                if (!interruptNet.sleep_for(\n                        std::chrono::milliseconds(std::min(time_to_sleep, 1000)))) {\n                    return;\n                }\n                boost::this_thread::interruption_point();\n                time_to_sleep = (nNextDandelionShuffle - GetTimeMicros()) / 1000;\n            }\n        }\n    }\n}\n\nvoid CConnman::SetNetworkActive(bool active)\n{\n    if (fDebug) {\n        LogPrint("net", "SetNetworkActive: %s\\n", active);\n    }\n\n    if (!active) {\n        fNetworkActive = false;\n\n        LOCK(cs_vNodes);\n        // Close sockets to all nodes\n        BOOST_FOREACH(CNode* pnode, vNodes) {\n            pnode->CloseSocketDisconnect();\n        }\n    } else {\n        fNetworkActive = true;\n    }\n\n    uiInterface.NotifyNetworkActiveChanged(fNetworkActive);\n}\n\nCConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In)\n{\n    fNetworkActive = true;\n    setBannedIsDirty = false;\n    fAddressesInitialized = false;\n    nLastNodeId = 0;\n    nSendBufferMaxSize = 0;\n    nReceiveFloodSize = 0;\n    semOutbound = NULL;\n    semAddnode = NULL;\n    semMasternodeOutbound = NULL;\n    nMaxConnections = 0;\n    nMaxOutbound = 0;\n    nMaxAddnode = 0;\n    nBestHeight = 0;\n    clientInterface = NULL;\n    flagInterruptMsgProc = false;\n}\n\nNodeId CConnman::GetNewNodeId()\n{\n    return nLastNodeId.fetch_add(1, std::memory_order_relaxed);\n}\n\nbool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options connOptions)\n{\n    nTotalBytesRecv = 0;\n    nTotalBytesSent = 0;\n    nMaxOutboundTotalBytesSentInCycle = 0;\n    nMaxOutboundCycleStartTime = 0;\n\n    nRelevantServices = connOptions.nRelevantServices;\n    nLocalServices = connOptions.nLocalServices;\n    nMaxConnections = connOptions.nMaxConnections;\n    nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections);\n    nMaxAddnode = connOptions.nMaxAddnode;\n    nMaxFeeler = connOptions.nMaxFeeler;\n\n    nSendBufferMaxSize = connOptions.nSendBufferMaxSize;\n    nReceiveFloodSize = connOptions.nReceiveFloodSize;\n\n    nMaxOutboundLimit = connOptions.nMaxOutboundLimit;\n    nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;\n\n    SetBestHeight(connOptions.nBestHeight);\n\n    clientInterface = connOptions.uiInterface;\n    if (clientInterface)\n        clientInterface->InitMessage(_("Loading addresses..."));\n    // Load addresses from peers.dat\n    int64_t nStart = GetTimeMillis();\n    {\n        CAddrDB adb;\n        if (adb.Read(addrman))\n            LogPrintf("Loaded %i addresses from peers.dat  %dms\\n", addrman.size(), GetTimeMillis() - nStart);\n        else {\n            addrman.Clear(); // Addrman can be in an inconsistent state after failure, reset it\n            LogPrintf("Invalid or missing peers.dat; recreating\\n");\n            DumpAddresses();\n        }\n    }\n    if (clientInterface)\n        clientInterface->InitMessage(_("Loading banlist..."));\n    // Load addresses from banlist.dat\n    nStart = GetTimeMillis();\n    CBanDB bandb;\n    banmap_t banmap;\n    if (bandb.Read(banmap)) {\n        SetBanned(banmap); // thread save setter\n        SetBannedSetDirty(false); // no need to write down, just read data\n        SweepBanned(); // sweep out unused entries\n\n        LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat  %dms\\n",\n            banmap.size(), GetTimeMillis() - nStart);\n    } else {\n        LogPrintf("Invalid or missing banlist.dat; recreating\\n");\n        SetBannedSetDirty(true); // force write\n        DumpBanlist();\n    }\n\n    uiInterface.InitMessage(_("Starting network threads..."));\n\n    fAddressesInitialized = true;\n\n    if (semOutbound == NULL) {\n        // initialize semaphore\n        semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));\n    }\n    if (semAddnode == NULL) {\n        // initialize semaphore\n        semAddnode = new CSemaphore(nMaxAddnode);\n    }\n    if (semMasternodeOutbound == NULL) {\n        // initialize semaphore\n        semMasternodeOutbound = new CSemaphore(fMasternodeMode ? MAX_OUTBOUND_MASTERNODE_CONNECTIONS_ON_MN : MAX_OUTBOUND_MASTERNODE_CONNECTIONS);\n    }\n\n    //\n    // Start threads\n    //\n    InterruptSocks5(false);\n    interruptNet.reset();\n    flagInterruptMsgProc = false;\n\n    {\n        std::unique_lock<std::mutex> lock(mutexMsgProc);\n        fMsgProcWake = false;\n    }\n\n    // Send and receive from sockets, accept connections\n    threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this)));\n\n    if (!GetBoolArg("-dnsseed", true))\n        LogPrintf("DNS seeding disabled\\n");\n    else\n        threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()> >, "dnsseed", std::function<void()>(std::bind(&CConnman::ThreadDNSAddressSeed, this)));\n\n    // Initiate outbound connections from -addnode\n    threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));\n\n    // Initiate outbound connections unless connect=0\n    if (!mapMultiArgs.count("-connect") || mapMultiArgs.at("-connect").size() != 1 || mapMultiArgs.at("-connect")[0] != "0")\n        threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this)));\n\n    // Initiate masternode connections\n    threadOpenMasternodeConnections = std::thread(&TraceThread<std::function<void()> >, "mncon", std::function<void()>(std::bind(&CConnman::ThreadOpenMasternodeConnections, this)));\n\n    // Process messages\n    threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));\n\n    // Dandelion shuffle\n    threadDandelionShuffle = std::thread(TraceThread<std::function<void()> >, "dandelion", std::function<void()>(std::bind(&CConnman::ThreadDandelionShuffle, this)));\n\n    // Dump network addresses\n    scheduler.scheduleEvery(boost::bind(&CConnman::DumpData, this), DUMP_ADDRESSES_INTERVAL);\n\n    return true;\n}\n\nclass CNetCleanup\n{\npublic:\n    CNetCleanup() {}\n\n    ~CNetCleanup()\n    {\n#ifdef WIN32\n        // Shutdown Windows Sockets\n        WSACleanup();\n#endif\n    }\n}\ninstance_of_cnetcleanup;\n\nvoid CConnman::Interrupt()\n{\n    {\n        std::lock_guard<std::mutex> lock(mutexMsgProc);\n        flagInterruptMsgProc = true;\n    }\n    condMsgProc.notify_all();\n\n    interruptNet();\n    InterruptSocks5(true);\n\n    if (semOutbound) {\n        for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {\n            semOutbound->post();\n        }\n    }\n\n    if (semAddnode) {\n        for (int i=0; i<nMaxAddnode; i++) {\n            semAddnode->post();\n        }\n    }\n\n    if (semMasternodeOutbound) {\n        int nMaxMasternodeOutbound = fMasternodeMode ? MAX_OUTBOUND_MASTERNODE_CONNECTIONS_ON_MN : MAX_OUTBOUND_MASTERNODE_CONNECTIONS;\n        for (int i = 0; i < nMaxMasternodeOutbound; i++) {\n            semMasternodeOutbound->post();\n        }\n    }\n}\n\nvoid CConnman::Stop()\n{\n    if (threadMessageHandler.joinable())\n        threadMessageHandler.join();\n    if (threadOpenMasternodeConnections.joinable())\n        threadOpenMasternodeConnections.join();\n    if (threadOpenConnections.joinable())\n        threadOpenConnections.join();\n    if (threadOpenAddedConnections.joinable())\n        threadOpenAddedConnections.join();\n    if (threadDNSAddressSeed.joinable())\n        threadDNSAddressSeed.join();\n    if (threadSocketHandler.joinable())\n        threadSocketHandler.join();\n    if (threadDandelionShuffle.joinable())\n        threadDandelionShuffle.join();\n\n    if (fAddressesInitialized)\n    {\n        DumpData();\n        fAddressesInitialized = false;\n    }\n\n    // Close sockets\n    BOOST_FOREACH(CNode* pnode, vNodes)\n        pnode->CloseSocketDisconnect();\n    BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)\n        if (hListenSocket.socket != INVALID_SOCKET)\n            if (!CloseSocket(hListenSocket.socket))\n                LogPrintf("CloseSocket(hListenSocket) failed with error %s\\n", NetworkErrorString(WSAGetLastError()));\n\n    // clean up some globals (to help leak detection)\n    BOOST_FOREACH(CNode *pnode, vNodes) {\n        DeleteNode(pnode);\n    }\n    BOOST_FOREACH(CNode *pnode, vNodesDisconnected) {\n        DeleteNode(pnode);\n    }\n    vNodes.clear();\n    vNodesDisconnected.clear();\n    vhListenSocket.clear();\n    delete semOutbound;\n    semOutbound = NULL;\n    delete semAddnode;\n    semAddnode = NULL;\n    delete semMasternodeOutbound;\n    semMasternodeOutbound = NULL;\n}\n\nvoid CConnman::DeleteNode(CNode* pnode)\n{\n    assert(pnode);\n    bool fUpdateConnectionTime = false;\n    GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime);\n    if(fUpdateConnectionTime)\n        addrman.Connected(pnode->addr);\n    delete pnode;\n}\n\nCConnman::~CConnman()\n{\n    Interrupt();\n    Stop();\n}\n\nsize_t CConnman::GetAddressCount() const\n{\n    return addrman.size();\n}\n\nvoid CConnman::SetServices(const CService &addr, ServiceFlags nServices)\n{\n    addrman.SetServices(addr, nServices);\n}\n\nvoid CConnman::MarkAddressGood(const CAddress& addr)\n{\n    addrman.Good(addr);\n}\n\nvoid CConnman::AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty)\n{\n    addrman.Add(addr, addrFrom, nTimePenalty);\n}\n\nvoid CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)\n{\n    addrman.Add(vAddr, addrFrom, nTimePenalty);\n}\n\nstd::vector<CAddress> CConnman::GetAddresses()\n{\n    return addrman.GetAddr();\n}\n\nbool CConnman::AddNode(const std::string& strNode)\n{\n    LOCK(cs_vAddedNodes);\n    for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {\n        if (strNode == *it)\n            return false;\n    }\n\n    vAddedNodes.push_back(strNode);\n    return true;\n}\n\nCNode* CNode::getDandelionDestination(CNode* pfrom) {\n    for (auto const& e : mDandelionRoutes) {\n        if (pfrom==e.first) {\n            return e.second;\n        }\n    }\n    CNode* newPto = CNode::SelectFromDandelionDestinations();\n    if (newPto != nullptr) {\n        mDandelionRoutes.insert(std::make_pair(pfrom, newPto));\n    }\n    return newPto;\n}\n\nvoid CNode::RelayDandelionTransaction(const CTransaction& tx, CNode* pfrom)\n{\n    if (!txpools.getStemTxPool().exists(tx.GetHash())) {\n        LogPrintf("ERROR: Trying to relay dandelion transaction %s which is not in the stempool.\\n",\n                  tx.GetHash().ToString());\n        return;\n    }\n    FastRandomContext rng;\n    const Consensus::Params& consensus = Params().GetConsensus();\n    if (rng.rand32() % 100 < consensus.nDandelionFluff) {\n        // Start fluffing current transaction.\n\n        CValidationState state;\n        std::shared_ptr<const CTransaction> ptx = txpools.getStemTxPool().get(tx.GetHash());\n        bool fMissingInputs = false;\n        std::list<CTransactionRef> lRemovedTxn;\n        AcceptToMemoryPool(\n            mempool,\n            state,\n            ptx,\n            true, // fLimitFree\n            &fMissingInputs, // pfMissingInputs\n            &lRemovedTxn,\n            false, /* fOverrideMempoolLimit */\n            0, /* nAbsurdFee */\n            false /*isCheckWalletTransaction*/\n            );\n        g_connman->RelayTransaction(tx);\n    } else {\n        // Relay transaction to a single dandelion destination.\n        CInv inv(MSG_DANDELION_TX, tx.GetHash());\n        CNode* destination = getDandelionDestination(pfrom);\n        if (destination!=nullptr) {\n            destination->PushInventory(inv);\n        }\n    }\n}\n\nvoid CNode::CheckDandelionEmbargoes()\n{\n    int64_t nCurrTime = GetTimeMicros();\n    for (auto iter=mDandelionEmbargo.begin(); iter != mDandelionEmbargo.end();) {\n        // If we got the embargoed transaction back, erase it.\n        if (mempool.exists(iter->first)) {\n           iter = mDandelionEmbargo.erase(iter);\n        } else if (iter->second < nCurrTime) {\n            // Embargo time is over, we did not "see" the transaction back in fluff phase,\n            // so start fluffing/relaying it.\n            CValidationState state;\n            std::shared_ptr<const CTransaction> ptx = txpools.getStemTxPool().get(iter->first);\n            // If txn was not found in Stempool, then something went wrong,\n            // Keep it embargoed for now.\n            if (!ptx) {\n               iter = mDandelionEmbargo.erase(iter);\n                continue;\n            }\n            bool fMissingInputs = false;\n            std::list<CTransactionRef> lRemovedTxn;\n            AcceptToMemoryPool(\n                mempool,\n                state,\n                ptx,\n                true, // fLimitFree\n                &fMissingInputs,\n                &lRemovedTxn,\n                false, /* fOverrideMempoolLimit */\n                0, /* nAbsurdFee */\n                false /*isCheckWalletTransaction*/\n                );\n            LogPrintf("AcceptToMemoryPool: accepted %s (poolsz %u txn, %u kB)\\n",\n                      iter->first.ToString(),\n                      mempool.size(),\n                      mempool.DynamicMemoryUsage() / 1000);\n            g_connman->RelayTransaction(*ptx);\n            iter = mDandelionEmbargo.erase(iter);\n        } else {\n            iter++;\n        }\n    }\n}\n\nbool CConnman::RemoveAddedNode(const std::string& strNode)\n{\n    LOCK(cs_vAddedNodes);\n    for(std::vector<std::string>::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {\n        if (strNode == *it) {\n            vAddedNodes.erase(it);\n            return true;\n        }\n    }\n    return false;\n}\n\nbool CConnman::AddPendingMasternode(const CService& service)\n{\n    LOCK(cs_vPendingMasternodes);\n    for(std::vector<CService>::const_iterator it = vPendingMasternodes.begin(); it != vPendingMasternodes.end(); ++it) {\n        if (service == *it)\n            return false;\n    }\n\n    vPendingMasternodes.push_back(service);\n    return true;\n}\n\nbool CConnman::AddMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes)\n{\n    LOCK(cs_vPendingMasternodes);\n    auto it = masternodeQuorumNodes.find(std::make_pair(llmqType, quorumHash));\n    if (it != masternodeQuorumNodes.end()) {\n        return false;\n    }\n    masternodeQuorumNodes.emplace(std::make_pair(llmqType, quorumHash), proTxHashes);\n    return true;\n}\n\nbool CConnman::HasMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)\n{\n    LOCK(cs_vPendingMasternodes);\n    return masternodeQuorumNodes.count(std::make_pair(llmqType, quorumHash));\n}\n\nstd::set<uint256> CConnman::GetMasternodeQuorums(Consensus::LLMQType llmqType)\n{\n    LOCK(cs_vPendingMasternodes);\n    std::set<uint256> result;\n    for (const auto& p : masternodeQuorumNodes) {\n        if (p.first.first != llmqType) {\n            continue;\n        }\n        result.emplace(p.first.second);\n    }\n    return result;\n}\n\nstd::set<NodeId> CConnman::GetMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash) const\n{\n    LOCK2(cs_vNodes, cs_vPendingMasternodes);\n    auto it = masternodeQuorumNodes.find(std::make_pair(llmqType, quorumHash));\n    if (it == masternodeQuorumNodes.end()) {\n        return {};\n    }\n    const auto& proRegTxHashes = it->second;\n\n    std::set<NodeId> nodes;\n    for (const auto pnode : vNodes) {\n        if (pnode->fDisconnect) {\n            continue;\n        }\n        if (!pnode->qwatch && (pnode->verifiedProRegTxHash.IsNull() || !proRegTxHashes.count(pnode->verifiedProRegTxHash))) {\n            continue;\n        }\n        nodes.emplace(pnode->id);\n    }\n    return nodes;\n}\n\nvoid CConnman::RemoveMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash)\n{\n    LOCK(cs_vPendingMasternodes);\n    masternodeQuorumNodes.erase(std::make_pair(llmqType, quorumHash));\n}\n\nbool CConnman::IsMasternodeQuorumNode(const CNode* pnode)\n{\n    // Let\'s see if this is an outgoing connection to an address that is known to be a masternode\n    // We however only need to know this if the node did not authenticate itself as a MN yet\n    uint256 assumedProTxHash;\n    if (pnode->verifiedProRegTxHash.IsNull() && !pnode->fInbound) {\n        auto mnList = deterministicMNManager->GetListAtChainTip();\n        auto dmn = mnList.GetMNByService(pnode->addr);\n        if (dmn == nullptr) {\n            // This is definitely not a masternode\n            return false;\n        }\n        assumedProTxHash = dmn->proTxHash;\n    }\n\n    LOCK(cs_vPendingMasternodes);\n    for (const auto& p : masternodeQuorumNodes) {\n        if (!pnode->verifiedProRegTxHash.IsNull()) {\n            if (p.second.count(pnode->verifiedProRegTxHash)) {\n                return true;\n            }\n        } else if (!assumedProTxHash.IsNull()) {\n            if (p.second.count(assumedProTxHash)) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\nsize_t CConnman::GetNodeCount(NumConnections flags)\n{\n    LOCK(cs_vNodes);\n    if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total\n        return vNodes.size();\n\n    int nNum = 0;\n    for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it)\n        if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))\n            nNum++;\n\n    return nNum;\n}\n\nvoid CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)\n{\n    vstats.clear();\n    LOCK(cs_vNodes);\n    vstats.reserve(vNodes.size());\n    for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) {\n        CNode* pnode = *it;\n        vstats.emplace_back();\n        pnode->copyStats(vstats.back());\n    }\n}\n\nbool CConnman::DisconnectNode(const std::string& strNode)\n{\n    LOCK(cs_vNodes);\n    if (CNode* pnode = FindNode(strNode)) {\n        pnode->fDisconnect = true;\n        return true;\n    }\n    return false;\n}\nbool CConnman::DisconnectNode(NodeId id)\n{\n    LOCK(cs_vNodes);\n    for(CNode* pnode : vNodes) {\n        if (id == pnode->id) {\n            pnode->fDisconnect = true;\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid CConnman::RelayTransaction(const CTransaction& tx)\n{\n    uint256 hash = tx.GetHash();\n    int nInv = MSG_TX;\n    CInv inv(nInv, hash);\n    LOCK(cs_vNodes);\n    BOOST_FOREACH(CNode* pnode, vNodes)\n    {\n        pnode->PushInventory(inv);\n    }\n}\n\nvoid CConnman::RelayInv(CInv &inv, const int minProtoVersion) {\n    LOCK(cs_vNodes);\n    for (const auto& pnode : vNodes)\n        if(pnode->nVersion >= minProtoVersion)\n            pnode->PushInventory(inv);\n}\n\nvoid CConnman::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx, const int minProtoVersion)\n{\n    LOCK(cs_vNodes);\n    for (const auto& pnode : vNodes) {\n        if(pnode->nVersion < minProtoVersion)\n            continue;\n        {\n            LOCK(pnode->cs_filter);\n            if(pnode->pfilter && !pnode->pfilter->IsRelevantAndUpdate(relatedTx))\n                continue;\n        }\n        pnode->PushInventory(inv);\n    }\n}\n\nvoid CConnman::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash, const int minProtoVersion)\n{\n    LOCK(cs_vNodes);\n    for (const auto& pnode : vNodes) {\n        if(pnode->nVersion < minProtoVersion) continue;\n        {\n            LOCK(pnode->cs_filter);\n            if(pnode->pfilter && !pnode->pfilter->contains(relatedTxHash)) continue;\n        }\n        pnode->PushInventory(inv);\n    }\n}\n\nvoid CConnman::RemoveAskFor(const uint256& hash)\n{\n    mapAlreadyAskedFor.erase(hash);\n\n    LOCK(cs_vNodes);\n    for (const auto& pnode : vNodes) {\n        pnode->RemoveAskFor(hash);\n    }\n}\n\nvoid CConnman::RecordBytesRecv(uint64_t bytes)\n{\n    LOCK(cs_totalBytesRecv);\n    nTotalBytesRecv += bytes;\n}\n\nvoid CConnman::RecordBytesSent(uint64_t bytes)\n{\n    LOCK(cs_totalBytesSent);\n    nTotalBytesSent += bytes;\n\n    uint64_t now = GetTime();\n    if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now)\n    {\n        // timeframe expired, reset cycle\n        nMaxOutboundCycleStartTime = now;\n        nMaxOutboundTotalBytesSentInCycle = 0;\n    }\n\n    // TODO, exclude whitebind peers\n    nMaxOutboundTotalBytesSentInCycle += bytes;\n}\n\nvoid CConnman::SetMaxOutboundTarget(uint64_t limit)\n{\n    LOCK(cs_totalBytesSent);\n    nMaxOutboundLimit = limit;\n}\n\nuint64_t CConnman::GetMaxOutboundTarget()\n{\n    LOCK(cs_totalBytesSent);\n    return nMaxOutboundLimit;\n}\n\nuint64_t CConnman::GetMaxOutboundTimeframe()\n{\n    LOCK(cs_totalBytesSent);\n    return nMaxOutboundTimeframe;\n}\n\nuint64_t CConnman::GetMaxOutboundTimeLeftInCycle()\n{\n    LOCK(cs_totalBytesSent);\n    if (nMaxOutboundLimit == 0)\n        return 0;\n\n    if (nMaxOutboundCycleStartTime == 0)\n        return nMaxOutboundTimeframe;\n\n    uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe;\n    uint64_t now = GetTime();\n    return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime();\n}\n\nvoid CConnman::SetMaxOutboundTimeframe(uint64_t timeframe)\n{\n    LOCK(cs_totalBytesSent);\n    if (nMaxOutboundTimeframe != timeframe)\n    {\n        // reset measure-cycle in case of changing\n        // the timeframe\n        nMaxOutboundCycleStartTime = GetTime();\n    }\n    nMaxOutboundTimeframe = timeframe;\n}\n\nbool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)\n{\n    LOCK(cs_totalBytesSent);\n    if (nMaxOutboundLimit == 0)\n        return false;\n\n    if (historicalBlockServingLimit)\n    {\n        // keep a large enough buffer to at least relay each block once\n        uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle();\n        uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SERIALIZED_SIZE;\n        if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer)\n            return true;\n    }\n    else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit)\n        return true;\n\n    return false;\n}\n\nuint64_t CConnman::GetOutboundTargetBytesLeft()\n{\n    LOCK(cs_totalBytesSent);\n    if (nMaxOutboundLimit == 0)\n        return 0;\n\n    return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle;\n}\n\nuint64_t CConnman::GetTotalBytesRecv()\n{\n    LOCK(cs_totalBytesRecv);\n    return nTotalBytesRecv;\n}\n\nuint64_t CConnman::GetTotalBytesSent()\n{\n    LOCK(cs_totalBytesSent);\n    return nTotalBytesSent;\n}\n\nServiceFlags CConnman::GetLocalServices() const\n{\n    return nLocalServices;\n}\n\nvoid CConnman::SetBestHeight(int height)\n{\n    nBestHeight.store(height, std::memory_order_release);\n}\n\nint CConnman::GetBestHeight() const\n{\n    return nBestHeight.load(std::memory_order_acquire);\n}\n\nunsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }\nunsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; }\n\nCNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string& addrNameIn, bool fInboundIn) :\n    nTimeConnected(GetSystemTimeInSeconds()),\n    nTimeFirstMessageReceived(0),\n    fFirstMessageIsMNAUTH(false),\n    addr(addrIn),\n    fInbound(fInboundIn),\n    id(idIn),\n    nKeyedNetGroup(nKeyedNetGroupIn),\n    addrKnown(5000, 0.001),\n    filterInventoryKnown(50000, 0.000001),\n    nLocalHostNonce(nLocalHostNonceIn),\n    nLocalServices(nLocalServicesIn),\n    nMyStartingHeight(nMyStartingHeightIn),\n    nSendVersion(0)\n{\n    nServices = NODE_NONE;\n    nServicesExpected = NODE_NONE;\n    hSocket = hSocketIn;\n    nRecvVersion = INIT_PROTO_VERSION;\n    nLastSend = 0;\n    nLastRecv = 0;\n    nSendBytes = 0;\n    nRecvBytes = 0;\n    nTimeOffset = 0;\n    addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;\n    nVersion = 0;\n    nNumWarningsSkipped = 0;\n    nLastWarningTime = 0;\n    strSubVer = "";\n    fWhitelisted = false;\n    fOneShot = false;\n    fAddnode = false;\n    fClient = false; // set by version message\n    fFeeler = false;\n    fSuccessfullyConnected = false;\n    fDisconnect = false;\n    nRefCount = 0;\n    nSendSize = 0;\n    nSendOffset = 0;\n    hashContinue = uint256();\n    nStartingHeight = -1;\n    filterInventoryKnown.reset();\n    fSendMempool = false;\n    fGetAddr = false;\n    nNextLocalAddrSend = 0;\n    nNextAddrSend = 0;\n    nNextInvSend = 0;\n    fRelayTxes = false;\n    fSentAddr = false;\n    pfilter = new CBloomFilter();\n    timeLastMempoolReq = 0;\n    nLastBlockTime = 0;\n    nLastTXTime = 0;\n    nPingNonceSent = 0;\n    nPingUsecStart = 0;\n    nPingUsecTime = 0;\n    fPingQueued = false;\n    nMinPingUsecTime = std::numeric_limits<int64_t>::max();\n    minFeeFilter = 0;\n    lastSentFeeFilter = 0;\n    nextSendTimeFeeFilter = 0;\n    // znode\n    fZnode = false;\n    fPauseRecv = false;\n    fPauseSend = false;\n    nProcessQueueSize = 0;\n    pendingMNVerification = nullptr;\n\n    BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())\n        mapRecvBytesPerMsgCmd[msg] = 0;\n    mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;\n\n    if (fLogIPs)\n        LogPrint("net", "Added connection to %s peer=%d\\n", addrName, id);\n    else\n        LogPrint("net", "Added connection peer=%d\\n", id);\n}\n\nCNode::~CNode()\n{\n    CloseSocket(hSocket);\n\n    if (pfilter)\n        delete pfilter;\n\n    if (pendingMNVerification)\n        delete pendingMNVerification;\n}\n\nvoid CNode::AskFor(const CInv& inv, int64_t doubleRequestDelay)\n{\n    if (vecAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) {\n        int64_t nNow = GetTime();\n        if(nNow - nLastWarningTime > WARNING_INTERVAL) {\n            LogPrintf("CNode::AskFor -- WARNING: inventory message dropped: vecAskFor.size = %d, setAskFor.size = %d, MAPASKFOR_MAX_SZ = %d, SETASKFOR_MAX_SZ = %d, nSkipped = %d, peer=%d\\n",\n                      vecAskFor.size(), setAskFor.size(), MAPASKFOR_MAX_SZ, SETASKFOR_MAX_SZ, nNumWarningsSkipped, id);\n            nLastWarningTime = nNow;\n            nNumWarningsSkipped = 0;\n        }\n        else {\n            ++nNumWarningsSkipped;\n        }\n        return;\n    }\n    // a peer may not have multiple non-responded queue positions for a single inv item\n    if (!setAskFor.insert(inv.hash).second)\n        return;\n\n    // We\'re using vecAskFor as a priority queue,\n    // the key is the earliest time the request can be sent\n    int64_t nRequestTime;\n    limitedmap<uint256, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv.hash);\n    if (it != mapAlreadyAskedFor.end())\n        nRequestTime = it->second;\n    else\n        nRequestTime = 0;\n    LogPrint("net", "askfor %s  %d (%s) peer=%d\\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id);\n\n    // Make sure not to reuse time indexes to keep things in the same order\n    int64_t nNow = GetTimeMicros() - 1000000;\n    static int64_t nLastTime;\n    ++nLastTime;\n    nNow = std::max(nNow, nLastTime);\n    nLastTime = nNow;\n\n    // Each retry is 2 minutes after the last\n    nRequestTime = std::max(nRequestTime + doubleRequestDelay, nNow);\n    if (it != mapAlreadyAskedFor.end())\n        mapAlreadyAskedFor.update(it, nRequestTime);\n    else\n        mapAlreadyAskedFor.insert(std::make_pair(inv.hash, nRequestTime));\n    vecAskFor.emplace_back(nRequestTime, inv);\n}\n\nvoid CNode::RemoveAskFor(const uint256& hash)\n{\n    if (setAskFor.erase(hash)) {\n        vecAskFor.erase(std::remove_if(vecAskFor.begin(), vecAskFor.end(), [&](const std::pair<int64_t, CInv>& item) {\n            return item.second.hash == hash;\n        }), vecAskFor.end());\n    }\n}\n\nbool CConnman::NodeFullyConnected(const CNode* pnode)\n{\n    return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect;\n}\n\nvoid CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg, bool allowOptimisticSend)\n{\n    size_t nMessageSize = msg.data.size();\n    size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE;\n    LogPrint("net", "sending %s (%d bytes) peer=%d\\n",  SanitizeString(msg.command.c_str()), nMessageSize, pnode->id);\n\n    std::vector<unsigned char> serializedHeader;\n    serializedHeader.reserve(CMessageHeader::HEADER_SIZE);\n    uint256 hash = Hash(msg.data.data(), msg.data.data() + nMessageSize);\n    CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), nMessageSize);\n    memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE);\n\n    CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, serializedHeader, 0, hdr};\n\n    size_t nBytesSent = 0;\n    {\n        LOCK(pnode->cs_vSend);\n        bool optimisticSend(allowOptimisticSend && pnode->vSendMsg.empty());\n\n        //log total amount of bytes per command\n        pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize;\n        pnode->nSendSize += nTotalSize;\n\n        if (pnode->nSendSize > nSendBufferMaxSize)\n            pnode->fPauseSend = true;\n        pnode->vSendMsg.push_back(std::move(serializedHeader));\n        if (nMessageSize)\n            pnode->vSendMsg.push_back(std::move(msg.data));\n\n        // If write queue empty, attempt "optimistic write"\n        if (optimisticSend == true)\n            nBytesSent = SocketSendData(pnode);\n    }\n    if (nBytesSent)\n        RecordBytesSent(nBytesSent);\n}\n\nbool CConnman::ForNode(const CService& addr, std::function<bool(const CNode* pnode)> cond, std::function<bool(CNode* pnode)> func)\n{\n    CNode* found = nullptr;\n    LOCK(cs_vNodes);\n    for (auto&& pnode : vNodes) {\n        if((CService)pnode->addr == addr) {\n            found = pnode;\n            break;\n        }\n    }\n    return found != nullptr && cond(found) && func(found);\n}\n\nbool CConnman::ForNode(NodeId id, std::function<bool(const CNode* pnode)> cond, std::function<bool(CNode* pnode)> func)\n{\n    CNode* found = nullptr;\n    LOCK(cs_vNodes);\n    for (auto&& pnode : vNodes) {\n        if(pnode->id == id) {\n            found = pnode;\n            break;\n        }\n    }\n    return found != nullptr && cond(found) && func(found);\n}\n\nstd::vector<CNode*> CConnman::CopyNodeVector(std::function<bool(const CNode* pnode)> cond)\n{\n    std::vector<CNode*> vecNodesCopy;\n    LOCK(cs_vNodes);\n    for(size_t i = 0; i < vNodes.size(); ++i) {\n        CNode* pnode = vNodes[i];\n        if (!cond(pnode))\n            continue;\n        pnode->AddRef();\n        vecNodesCopy.push_back(pnode);\n    }\n    return vecNodesCopy;\n}\n\nstd::vector<CNode*> CConnman::CopyNodeVector()\n{\n    return CopyNodeVector(AllNodes);\n}\n\nvoid CConnman::ReleaseNodeVector(const std::vector<CNode*>& vecNodes)\n{\n    LOCK(cs_vNodes);\n    for(size_t i = 0; i < vecNodes.size(); ++i) {\n        CNode* pnode = vecNodes[i];\n        pnode->Release();\n    }\n}\n\nbool CConnman::IsMasternodeOrDisconnectRequested(const CService& addr) {\n    return ForNode(addr, AllNodes, [](CNode* pnode){\n        return pnode->fZnode || pnode->fDisconnect;\n    });\n}\n\nint64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {\n    return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);\n}\n\nCSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const\n{\n    return CSipHasher(nSeed0, nSeed1).Write(id);\n}\n\nuint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const\n{\n    std::vector<unsigned char> vchNetGroup(ad.GetGroup());\n\n    return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();\n}\n\n// Dandelion\nbool CNode::isDandelionInbound(const CNode* const pnode)\n{\n    return (std::find(vDandelionInbound.begin(), vDandelionInbound.end(), pnode) != vDandelionInbound.end());\n}\n\nbool CNode::isLocalDandelionDestinationSet()\n{\n    return (localDandelionDestination != nullptr);\n}\n\nbool CNode::setLocalDandelionDestination()\n{\n    if (!isLocalDandelionDestinationSet()) {\n        localDandelionDestination = CNode::SelectFromDandelionDestinations();\n    }\n    return isLocalDandelionDestinationSet();\n}\n\nbool CNode::localDandelionDestinationPushInventory(const CInv& inv) {\n    if (!isLocalDandelionDestinationSet()) {\n        setLocalDandelionDestination();\n    }\n    if (isLocalDandelionDestinationSet()) {\n        localDandelionDestination->PushInventory(inv);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool CNode::insertDandelionEmbargo(const uint256& hash, const int64_t& embargo) {\n    auto pair = mDandelionEmbargo.insert(std::make_pair(hash, embargo));\n    return pair.second;\n}\n\nbool CNode::isTxDandelionEmbargoed(const uint256& hash) {\n    return mDandelionEmbargo.find(hash) != mDandelionEmbargo.end();\n}\n\nbool CNode::removeDandelionEmbargo(const uint256& hash) {\n    auto iter = mDandelionEmbargo.find(hash);\n    if (iter != mDandelionEmbargo.end()) {\n        mDandelionEmbargo.erase(iter);\n        return true;\n    }\n    return false;\n}\n', 'message': 'Merge pull request #5971\n\n351593b replace absolute sleep with conditional wait (pstratem)\n', 'author_name': 'Wladimir J. van der Laan', 'lang': 'cpp'}