text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2012 The Bitcoin developers
  // Copyright (c) 2015-2021 Bean Core www.beancash.org
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "db.h"
  #include "net.h"
  #include "main.h"
  #include "addrman.h"
  #include "ui_interface.h"
  
  #ifdef WIN32
  #include <string.h>
  #endif
  
  using namespace std;
  using namespace boost;
  
  static const int MAX_OUTBOUND_CONNECTIONS = 32;
  
  bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
  
  
  struct LocalServiceInfo {
      int nScore;
      int nPort;
  };
  
  //
  // Global state variables
  //
  bool fClient = false;
  bool fDiscover = true;
  uint64_t nLocalServices = (fClient ? 0 : NODE_NETWORK);
  static CCriticalSection cs_mapLocalHost;
  static map<CNetAddr, LocalServiceInfo> mapLocalHost;
  static bool vfReachable[NET_MAX] = {};
  static bool vfLimited[NET_MAX] = {};
  static CNode* pnodeLocalHost = NULL;
  static CNode* pnodeSync = NULL;
  uint64_t nLocalHostNonce = 0;
  static std::vector<SOCKET> vhListenSocket;
  CAddrMan addrman;
  int nMaxConnections = 200;
  bool fImporting = false;
  
  vector<CNode*> vNodes;
  CCriticalSection cs_vNodes;
  map<CInv, CDataStream> mapRelay;
  deque<pair<int64_t, CInv> > vRelayExpiration;
  CCriticalSection cs_mapRelay;
  limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
  
  static deque<string> vOneShots;
  CCriticalSection cs_vOneShots;
  
  set<CNetAddr> setservAddNodeAddresses;
  CCriticalSection cs_setservAddNodeAddresses;
  
  vector<std::string> vAddedNodes;
  CCriticalSection cs_vAddedNodes;
  
  // Signals for message handling
  static CNodeSignals g_signals;
  CNodeSignals& GetNodeSignals() { return g_signals;}
  
  static CSemaphore *semOutbound = NULL;
  
  void AddOneShot(string strDest)
  {
      LOCK(cs_vOneShots);
      vOneShots.push_back(strDest);
  }
  
  unsigned short GetListenPort()
  {
      return (unsigned short)(GetArg("-port", GetDefaultPort()));
  }
  
  // find 'best' local address for a particular peer
  bool GetLocal(CService& addr, const CNetAddr* paddrPeer)
  {
      if (fNoListen)
          return false;
  
      int nBestScore = -1;
      int nBestReachability = -1;
      {
          LOCK(cs_mapLocalHost);
          for (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;
  }
  
  // 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)
  {
      CAddress ret(CService("0.0.0.0", GetListenPort()), 0);
      CService addr;
      if (GetLocal(addr, paddrPeer))
      {
          ret = CAddress(addr);
      }
      ret.nServices = nLocalServices;
      ret.nTime = GetAdjustedTime();
      return ret;
  }
  
  bool RecvLine(SOCKET hSocket, string& strLine)
  {
      strLine = "";
      while (true)
      {
          char c;
          int nBytes = recv(hSocket, &c, 1, 0);
          if (nBytes > 0)
          {
              if (c == '\n')
                  continue;
              if (c == '\r')
                  return true;
              strLine += c;
              if (strLine.size() >= 9000)
                  return true;
          }
          else if (nBytes <= 0)
          {
              boost::this_thread::interruption_point();
              if (nBytes < 0)
              {
                  int nErr = WSAGetLastError();
                  if (nErr == WSAEMSGSIZE)
                      continue;
                  if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
                  {
                      MilliSleep(10);
                      continue;
                  }
              }
              if (!strLine.empty())
                  return true;
              if (nBytes == 0)
              {
                  // socket closed
                  LogPrint("net", "socket closed\n");
                  return false;
              }
              else
              {
                  // socket error
                  int nErr = WSAGetLastError();
                  LogPrint("net", "recv failed: %d\n", nErr);
                  return false;
              }
          }
      }
  }
  
  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)
  {
      return fDiscover && pnode->addr.IsRoutable() &&
  pnode->addrLocal.IsRoutable() &&
  	!IsLimited(pnode->addrLocal.GetNetwork());
  }
  
  // pushes our own address to a peer
  void AdvertiseLocal(CNode* pnode)
  {
      if (!fNoListen && pnode->fSuccessfullyConnected)
      {
          CAddress addrLocal = GetLocalAddress(&pnode->addr);
  	// 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->addrLocal);
          }
  
          if (addrLocal.IsRoutable())
          {
              pnode->PushAddress(addrLocal);
          }
      }
  }
  
  void SetReachable(enum Network net, bool fFlag)
  {
      LOCK(cs_mapLocalHost);
      vfReachable[net] = fFlag;
      if (net == NET_IPV6 && fFlag)
          vfReachable[NET_IPV4] = true;
  }
  
  // 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();
          }
          SetReachable(addr.GetNetwork());
      }
  
      return true;
  }
  
  bool AddLocal(const CNetAddr& addr, int nScore)
  {
      return AddLocal(CService(addr, GetListenPort()), nScore);
  }
  
  /** 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 address is in a network we can probably connect to */
  bool IsReachable(const CNetAddr& addr)
  {
      LOCK(cs_mapLocalHost);
      enum Network net = addr.GetNetwork();
      return vfReachable[net] && !vfLimited[net];
  }
  
  void AddressCurrentlyConnected(const CService& addr)
  {
      addrman.Connected(addr);
  }
  
  uint64_t CNode::nTotalBytesRecv = 0;
  uint64_t CNode::nTotalBytesSent = 0;
  CCriticalSection CNode::cs_totalBytesRecv;
  CCriticalSection CNode::cs_totalBytesSent;
  
  CNode* FindNode(const CNetAddr& ip)
  {
      {
          LOCK(cs_vNodes);
          for (CNode* pnode : vNodes)
              if ((CNetAddr)pnode->addr == ip)
                  return (pnode);
      }
      return NULL;
  }
  
  CNode* FindNode(const std::string& addrName)
  {
      LOCK(cs_vNodes);
      for (CNode* pnode : vNodes)
          if (pnode->addrName == addrName)
              return (pnode);
      return NULL;
  }
  
  CNode* FindNode(const CService& addr)
  {
      {
          LOCK(cs_vNodes);
          for (CNode* pnode : vNodes)
              if ((CService)pnode->addr == addr)
                  return (pnode);
      }
      return NULL;
  }
  
  CNode* ConnectNode(CAddress addrConnect, const char* pszDest)
  {
      if (pszDest == NULL)
      {
          if (IsLocal(addrConnect))
              return NULL;
  
          // Look for an existing connection
          CNode* pnode = FindNode((CService)addrConnect);
          if (pnode)
          {
              pnode->AddRef();
              return pnode;
          }
      }
  
  
      /// debug print
      LogPrint("net", "trying connection %s lastseen=%.1fhrs\n",
          pszDest ? pszDest : addrConnect.ToString(),
          pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
  
      // Connect
      SOCKET hSocket;
      if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket))
      {
          addrman.Attempt(addrConnect);
  
          /// debug print
          LogPrint("net", "connected %s\n", pszDest ? pszDest : addrConnect.ToString());
  
          // Set to non-blocking
  #ifdef WIN32
          u_long nOne = 1;
          if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
              LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
  #else
          if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
              LogPrintf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
  #endif
  
          // Add node
          CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
          pnode->AddRef();
  
          {
              LOCK(cs_vNodes);
              vNodes.push_back(pnode);
          }
  
          pnode->nTimeConnected = GetTime();
          return pnode;
      }
      else
      {
          return NULL;
      }
  }
  
  void CNode::CloseSocketDisconnect()
  {
      fDisconnect = true;
      if (hSocket != INVALID_SOCKET)
      {
          LogPrint("net", "disconnecting node %s\n", addrName);
          closesocket(hSocket);
          hSocket = INVALID_SOCKET;
  
          // in case this fails, we'll empty the recv buffer when the CNode is deleted
          TRY_LOCK(cs_vRecvMsg, lockRecv);
          if (lockRecv)
              vRecvMsg.clear();
      }
  }
  
  void CNode::Cleanup()
  {
  }
  
  
  void CNode::PushVersion()
  {
      /// when NTP implemented, change to just nTime = GetAdjustedTime()
      int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
      CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0)));
      CAddress addrMe = GetLocalAddress(&addr);
      RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
      LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString());
      PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
                  nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight);
  }
  
  
  
  
  
  std::map<CNetAddr, int64_t> CNode::setBanned;
  CCriticalSection CNode::cs_setBanned;
  
  void CNode::ClearBanned()
  {
      setBanned.clear();
  }
  
  bool CNode::IsBanned(CNetAddr ip)
  {
      bool fResult = false;
      {
          LOCK(cs_setBanned);
          std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
          if (i != setBanned.end())
          {
              int64_t t = (*i).second;
              if (GetTime() < t)
                  fResult = true;
          }
      }
      return fResult;
  }
  
  bool CNode::Misbehaving(int howmuch)
  {
      if (addr.IsLocal())
      {
          LogPrintf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName, howmuch);
          return false;
      }
  
      nMisbehavior += howmuch;
      if (nMisbehavior >= GetArg("-banscore", 100))
      {
          int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24);  // Default 24-hour ban
          LogPrintf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString(), nMisbehavior-howmuch, nMisbehavior);
          {
              LOCK(cs_setBanned);
              if (setBanned[addr] < banTime)
                  setBanned[addr] = banTime;
          }
          CloseSocketDisconnect();
          return true;
      } else
          LogPrintf("Misbehaving: %s (%d -> %d)\n", addr.ToString(), nMisbehavior-howmuch, nMisbehavior);
      return false;
  }
  
  #undef X
  #define X(name) stats.name = name
  void CNode::copyStats(CNodeStats& stats)
  {
      X(nServices);
      X(nLastSend);
      X(nLastRecv);
      X(nTimeConnected);
      X(addrName);
      X(nVersion);
      X(strSubVer);
      X(fInbound);
      X(nStartingHeight);
      X(nMisbehavior);
      X(nSendBytes);
      X(nRecvBytes);
      stats.fSyncNode = (this == pnodeSync);
  
      // 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.dPingWait = (((double)nPingUsecWait) / 1e6);
  }
  #undef X
  
  // requires LOCK(cs_vRecvMsg)
  bool CNode::ReceiveMsgBytes(const char* pch, unsigned int nBytes)
  {
      while (nBytes > 0) {
  
          // get current incomplete message, or create a new one
          if (vRecvMsg.empty() ||
              vRecvMsg.back().complete())
              vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion));
  
          CNetMessage& msg = vRecvMsg.back();
  
          // absorb network data
          int handled;
          if (!msg.in_data)
              handled = msg.readHeader(pch, nBytes);
          else
              handled = msg.readData(pch, nBytes);
  
          if (handled < 0)
                  return false;
  
          pch += handled;
          nBytes -= handled;
  
          if (msg.complete())
              msg.nTime = GetTimeMicros();
      }
  
      return true;
  }
  
  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 512 KB ahead, but not more than the message size
          vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 512 * 1024));
      }
  
      memcpy(&vRecv[nDataPos], pch, nCopy);
      nDataPos += nCopy;
  
      return nCopy;
  }
  
  
  // requires LOCK(cs_vSend)
  int SocketSendData(CNode* pnode)
  {
      int progress = 0;
  
      // Make sure we haven't already been asked to disconnect
      if (pnode->fDisconnect)
          return progress;
  
      std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();
      while (it != pnode->vSendMsg.end())
      {
          const CSerializeData& data = *it;
          assert(data.size() > pnode->nSendOffset);
  
          int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
          if (nBytes > 0) {
              progress++;
              pnode->nLastSend = GetTime();
              pnode->nSendBytes += nBytes;
              pnode->nSendOffset += nBytes;
              pnode->RecordBytesSent(nBytes);
              if (pnode->nSendOffset == data.size()) {
                  pnode->nSendOffset = 0;
                  pnode->nSendSize -= data.size();
                  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 %d\n", 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 progress;
  }
  
  static list<CNode*> vNodesDisconnected;
  
  void ThreadSocketHandler()
  {
      unsigned int nPrevNodeCount = 0;
      int progress;
      while (true)
      {
          progress =0;
          //
          // Disconnect nodes
          //
          {
              LOCK(cs_vNodes);
              // Disconnect unused nodes
              vector<CNode*> vNodesCopy = vNodes;
              for (CNode* pnode : vNodesCopy)
              {
                  if (pnode->fDisconnect ||
                      (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty()))
                  {
                      // remove from vNodes
                      vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
  
                      // release outbound grant (if any)
                      pnode->grantOutbound.Release();
  
                      // close socket and cleanup
                      pnode->CloseSocketDisconnect();
                      pnode->Cleanup();
  
                      // hold in disconnected pool until all refs are released
                      if (pnode->fNetworkNode || pnode->fInbound)
                          pnode->Release();
                      vNodesDisconnected.push_back(pnode);
                  }
              }
          } // Process node deletions outside of cs_vNodes
          { //
              // Delete disconnected nodes
              list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
              for (CNode* pnode : vNodesDisconnectedCopy)
              {
                  // wait until threads are done using it
                  if (pnode->GetRefCount() <= 0)
                  {
                      bool fDelete = false;
                      {
                          TRY_LOCK(pnode->cs_vSend, lockSend);
                          if (lockSend)
                          {
                              TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                              if (lockRecv)
                              {
                              	  // Part of Satoshi's "checkorder" P2P command - Peer-to-Peer Electronic Cash!
                                  TRY_LOCK(pnode->cs_mapRequests, lockReq);
                                  if (lockReq)
                                  {
                                      TRY_LOCK(pnode->cs_inventory, lockInv);
                                      if (lockInv)
                                          fDelete = true;
                                  }
                                  // End part of Satoshi's "checkorder" P2P command - Peer-to-Peer Electronic Cash!
                              }
                          }
                      }
                      if (fDelete)
                      {
                          vNodesDisconnected.remove(pnode);
                          delete pnode;
                      }
                  }
              }
          }
          if (vNodes.size() != nPrevNodeCount) {
              nPrevNodeCount = vNodes.size();
              uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
          }
  
  
          //
          // 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;
  
          for (SOCKET hListenSocket : vhListenSocket)
          {
              FD_SET(hListenSocket, &fdsetRecv);
              hSocketMax = max(hSocketMax, hListenSocket);
              have_fds = true;
          }
  
          {
              LOCK(cs_vNodes);
              for (CNode* pnode : vNodes)
              {
                  if (pnode->hSocket == INVALID_SOCKET)
                      continue;
                  FD_SET(pnode->hSocket, &fdsetError);
                  hSocketMax = max(hSocketMax, pnode->hSocket);
                  have_fds = true;
                  {
                      TRY_LOCK(pnode->cs_vSend, lockSend);
                      if (lockSend && !pnode->vSendMsg.empty())
                      {
                          FD_SET(pnode->hSocket, &fdsetSend);
                          continue;
                      }
                  }
                  {
                      TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                      if (lockRecv && (pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || pnode->GetTotalRecvSize() <= ReceiveFloodSize()))
                          FD_SET(pnode->hSocket, &fdsetRecv);
                  }
              }
          }
  
          int nSelect = select(have_fds ? hSocketMax + 1 : 0,
                               &fdsetRecv,
                               &fdsetSend,
                               &fdsetError,
                               &timeout);
          boost::this_thread::interruption_point();
  
          if (nSelect == SOCKET_ERROR)
          {
              if (have_fds)
              {
                  int nErr = WSAGetLastError();
                  LogPrintf("socket select error %d\n", nErr);
                  for (unsigned int i = 0; i <= hSocketMax; i++)
                      FD_SET(i, &fdsetRecv);
              }
              FD_ZERO(&fdsetSend);
              FD_ZERO(&fdsetError);
              MilliSleep(timeout.tv_usec/1000);
          }
  
  
          //
          // Accept new connections
          //
          for (SOCKET hListenSocket : vhListenSocket)
          if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
          {
              struct sockaddr_storage sockaddr;
              socklen_t len = sizeof(sockaddr);
              SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
              CAddress addr;
              int nInbound = 0;
  
              if (hSocket != INVALID_SOCKET)
                  if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
                      LogPrintf("Warning: Unknown socket family\n");
  
              {
                  LOCK(cs_vNodes);
                  for (CNode* pnode : vNodes)
                      if (pnode->fInbound)
                          nInbound++;
              }
  
              if (hSocket == INVALID_SOCKET)
              {
                  int nErr = WSAGetLastError();
                  if (nErr != WSAEWOULDBLOCK)
                      LogPrintf("socket error accept failed: %d\n", nErr);
              }
              else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS)
              {
                  closesocket(hSocket);
              }
              else if (CNode::IsBanned(addr))
              {
                  LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
                  closesocket(hSocket);
              }
              else
              {
                  LogPrint("net", "accepted connection %s\n", addr.ToString());
                  CNode* pnode = new CNode(hSocket, addr, "", true);
                  pnode->AddRef();
                  {
                      LOCK(cs_vNodes);
                      vNodes.push_back(pnode);
                  }
              }
          }
  
  
          //
          // Service each socket
          //
          vector<CNode*> vNodesCopy;
          {
              LOCK(cs_vNodes);
              vNodesCopy = vNodes;
              for (CNode* pnode : vNodesCopy)
                  pnode->AddRef();
          }
          for (CNode* pnode : vNodesCopy)
          {
              boost::this_thread::interruption_point();
  
              //
              // Receive
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
              {
                  TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                  if (lockRecv)
                  {
                      // typical socket buffer is 8K-64K
                      char pchBuf[0x10000];
                      progress++;
                      int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
                      if (nBytes > 0)
                      {
                          if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
                          {
                              pnode->CloseSocketDisconnect();
                          }
                          pnode->nLastRecv = GetTime();
                          pnode->nRecvBytes += nBytes;
                          pnode->RecordBytesRecv(nBytes);
                      }
                      else if (nBytes == 0)
                      {
                          // socket closed gracefully
                          if (!pnode->fDisconnect)
                          {
                              LogPrint("net", "socket closed\n");
                              pnode->CloseSocketDisconnect();
                              continue;
                          }
                      }
                      else if (nBytes < 0)
                      {
                          // error
                          int nErr = WSAGetLastError();
                          if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
                          {
                              if (!pnode->fDisconnect)
                              {
                                  LogPrintf("socket recv error %d\n", nErr);
                                  pnode->CloseSocketDisconnect();
                                  continue;
                              }
                          }
                      }
                  }
              }
  
              //
              // Send
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetSend))
              {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                  {
                      progress += SocketSendData(pnode);
                  }
              }
  
              //
              // Inactivity checking
              //
              int64_t nTime = GetTime();
              if (nTime - pnode->nTimeConnected > 60)
              {
                  if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
                  {
                      LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
                      pnode->fDisconnect = true;
                  }
                  else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
                  {
                      LogPrintf("socket sending timeout: %ds\n", nTime - pnode->nLastSend);
                      pnode->fDisconnect = true;
                  }
                  else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
                  {
                      LogPrintf("socket receive timeout: %ds\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;
                  }
              }
          }
          {
              LOCK(cs_vNodes);
              for (CNode* pnode : vNodesCopy)
                  pnode->Release();
          }
  
              if (progress == 0) // Nothing happened even though select did not block, so slow down.
                  MilliSleep(50);
      }
  }
  
  
  // DNS seeds
  // Each pair gives a source name and a seed name.
  // The first name is used as information source for addrman.
  // The second name should resolve to a list of seed addresses.
  static const char *strDNSSeed[][2] = {
      {"bitbean.org", "stalk1.bitbean.org"},
      {"bitbean.org", "stalk2.bitbean.org"},
      {"bitbean.org", "stalk3.bitbean.org"},
      {"beancash.org", "stalk1.beancash.org"},
      {"beancash.org", "stalk2.beancash.org"},
      {"beancash.org", "stalk3.beancash.org"},
      {"beancash.is", "stalk1.beancash.is"},
      {"beancash.is", "stalk2.beancash.is"},
      {"beancash.is", "stalk3.beancash.is"}
  };
  
  
  void ThreadDNSAddressSeed()
  {
      // Only query DNS seeders if needed
      if ((addrman.size() > 0) &&
          (!GetBoolArg("-forceddnsseed", false))) {
          MilliSleep(11 * 1000);
  
          LOCK(cs_vNodes);
          if (vNodes.size() >= 2) {
              LogPrintf("P2P peers are available, so skipping DNS seeders.\n");
              return;
          }
      }
  
      int found = 0;
  
      if (!fTestNet)
      {
          LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
  
          for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
              if (HaveNameProxy()) {
                  AddOneShot(strDNSSeed[seed_idx][1]);
              } else {
                  vector<CNetAddr> vaddr;
                  vector<CAddress> vAdd;
                  if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
                  {
                      for (CNetAddr& ip : vaddr)
                      {
                          int nOneDay = 24*3600;
                          CAddress addr = CAddress(CService(ip, GetDefaultPort()));
                          addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
                          vAdd.push_back(addr);
                          found++;
                      }
                  }
                  addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true));
              }
          }
      }
  
      LogPrintf("%d addresses found from DNS seeds\n", found);
  }
  
      unsigned int pnSeed[] =
      {
          0x13be96b9, 0x5282a359, 0x8d3b7dae, 0x9c98a359,
          0x1482c705, 0x0a99365e, 0x9398a359, 0x4c6c6805,
          0xec0402bc, 0x8298a359, 0x476c6805, 0x4032b75f,
          0x4551f388, 0x749dff33, 0xf508504f, 0x6e52cb74,
          0x563634b9, 0xaa4ed95f, 0x7e658a02, 0x81a5f4dc,
          0x93fe12b2, 0x35ed0252, 0x615056a7, 0x615056a7,
          0x0e47a868
  
      };
  
  
  void 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 static ProcessOneShot()
  {
      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, &grant, strDest.c_str(), true))
              AddOneShot(strDest);
      }
  }
  
  void ThreadOpenConnections()
  {
      // Connect to specific addresses
      if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
      {
          for (int64_t nLoop = 0;; nLoop++)
          {
              ProcessOneShot();
              for (string strAddr : mapMultiArgs["-connect"])
              {
                  CAddress addr;
                  OpenNetworkConnection(addr, NULL, strAddr.c_str());
                  for (int i = 0; i < 10 && i < nLoop; i++)
                  {
                      MilliSleep(100);
                  }
              }
              MilliSleep(100);
          }
      }
  
      // Initiate network connections
      int64_t nStart = GetTime();
      while (true)
      {
          ProcessOneShot();
  
          MilliSleep(100);
  
          CSemaphoreGrant grant(*semOutbound);
          boost::this_thread::interruption_point();
  
          // Add Beanstalk nodes
          if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet)
          {
              std::vector<CAddress> vAdd;
              for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
              {
                  // 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;
                  struct in_addr ip;
                  memcpy(&ip, &pnSeed[i], sizeof(ip));
                  CAddress addr(CService(ip, GetDefaultPort()));
                  addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek;
                  vAdd.push_back(addr);
              }
              addrman.Add(vAdd, CNetAddr("127.0.0.1"));
          }
  
          //
          // 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;
          set<vector<unsigned char> > setConnected;
          {
              LOCK(cs_vNodes);
              for (CNode* pnode : vNodes) {
                  if (!pnode->fInbound) {
                      setConnected.insert(pnode->addr.GetGroup());
                      nOutbound++;
                  }
              }
          }
  
          int64_t nANow = GetAdjustedTime();
  
          int nTries = 0;
          while (true)
          {
              CAddress addr = addrman.Select();
  
              // 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 > 50)
                  break;
  
              if (IsLimited(addr))
                  continue;
  
              // only consider very recently tried nodes after 30 failed attempts
              if (nANow - addr.nLastTry < 600 && nTries < 15)
                  continue;
  
              // do not allow non-default ports, unless after 50 invalid addresses selected already
              if (addr.GetPort() != GetDefaultPort() && nTries < 30)
                  continue;
  
              addrConnect = addr;
              break;
          }
  
          if (addrConnect.IsValid())
              OpenNetworkConnection(addrConnect, &grant);
      }
  }
  
  void ThreadOpenAddedConnections()
  {
      {
         LOCK(cs_vAddedNodes);
         vAddedNodes = mapMultiArgs["-addnode"];
      }
  
      if (HaveNameProxy()) {
          while(true) {
              list<string> lAddresses(0);
  
              {
                  LOCK(cs_vAddedNodes);
                  for (string& strAddNode : vAddedNodes)
                          lAddresses.push_back(strAddNode);
              }
              for (string& strAddNode : lAddresses) {
                  CAddress addr;
                  CSemaphoreGrant grant(*semOutbound);
                  OpenNetworkConnection(addr, &grant, strAddNode.c_str());
                  MilliSleep(100);
              }
              MilliSleep(60000); // Retry every minute
          }
      }
  
      for (unsigned int i = 0; true; i++)
      {
          list<string> lAddresses(0);
          {
              LOCK(cs_vAddedNodes);
              for (string& strAddNode : vAddedNodes)
                      lAddresses.push_back(strAddNode);
          }
  
          list<vector<CService> > lservAddressesToAdd(0);
          for (string& strAddNode : lAddresses)
          {
              vector<CService> vservNode(0);
              if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0))
              {
                  lservAddressesToAdd.push_back(vservNode);
                  {
                      LOCK(cs_setservAddNodeAddresses);
                      for (CService& serv : vservNode)
                              setservAddNodeAddresses.insert(serv);
                  }
              }
          }
  
          // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry
          // (keeping in mind that addnode entries can have many IPs if fNameLookup)
          {
              LOCK(cs_vNodes);
              for (CNode* pnode : vNodes)
                  for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++)
                      for (CService& addrNode : *(it))
                          if (pnode->addr == addrNode)
                          {
                              it = lservAddressesToAdd.erase(it);
                              it--;
                              break;
                          }
          }
          for (vector<CService>& vserv : lservAddressesToAdd)
          {
              CSemaphoreGrant grant(*semOutbound);
              OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant);
              MilliSleep(100);
          }
          MilliSleep(60000); // Retry every minute
      }
  }
  
  // if successful, this moves the passed grant to the constructed node
  bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot)
  {
      //
      // Initiate outbound network connection
      //
      boost::this_thread::interruption_point();
      if (!strDest)
          if (IsLocal(addrConnect) ||
              FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
              FindNode(addrConnect.ToStringIPPort().c_str()))
              return false;
      if (strDest && FindNode(strDest))
          return false;
  
      CNode* pnode = ConnectNode(addrConnect, strDest);
      boost::this_thread::interruption_point();
      if (!pnode)
          return false;
      if (grantOutbound)
          grantOutbound->MoveTo(pnode->grantOutbound);
      pnode->fNetworkNode = true;
      if (fOneShot)
          pnode->fOneShot = true;
  
      return true;
  }
  
  void ThreadMessageHandler()
  {
      SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
      while (true)
      {
          vector<CNode*> vNodesCopy;
          {
              LOCK(cs_vNodes);
              vNodesCopy = vNodes;
              for (CNode* pnode : vNodesCopy)
                  pnode->AddRef();
          }
  
          // Poll the connected nodes for messages
          CNode* pnodeTrickle = NULL;
          if (!vNodesCopy.empty())
              pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
              
  		  bool fSleep = true;            
              
          for (CNode* pnode : vNodesCopy)
          {
              if (pnode->fDisconnect)
                  continue;
  
              // Receive messages
              {
                  TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                  if (lockRecv)
                  {
                      if (!g_signals.ProcessMessages(pnode))
                          pnode->CloseSocketDisconnect();
                          
                      if (pnode->nSendSize < SendBufferSize())
                      {
  								if(!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete()))
  								{
  									fSleep = false;								
  								}                    
                      }
                  }
              }
              boost::this_thread::interruption_point();
  
              // Send messages
              {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                      g_signals.SendMessages(pnode, pnode == pnodeTrickle);
              }
              boost::this_thread::interruption_point();
          }
  
          {
              LOCK(cs_vNodes);
              for (CNode* pnode : vNodesCopy)
                  pnode->Release();
          }
  
          if (fSleep)
          		MilliSleep(100);
      }
  }
  
  
  
  
  
  
  bool BindListenPort(const CService &addrBind, string& strError)
  {
      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 %d)", WSAGetLastError());
          LogPrintf("%s\n", strError);
          return false;
      }
  
  #ifdef SO_NOSIGPIPE
      // Different way of disabling SIGPIPE on BSD
      setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
  #endif
  
  #ifndef WIN32
      // Allow binding if the port is still in TIME_WAIT state after
      // the program was closed and restarted.  Not an issue on windows.
      setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
  #endif
  
  
  #ifdef WIN32
      // Set to non-blocking, incoming connections will also inherit this
      if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
  #else
      if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
  #endif
      {
          strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", 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 = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
          int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
          // this call is allowed to fail
          setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (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. Bean Cash is probably already running."), addrBind.ToString());
          else
              strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString(), nErr, strerror(nErr));
          LogPrintf("%s\n", strError);
          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 %d)", WSAGetLastError());
          LogPrintf("%s\n", strError);
          return false;
      }
  
      vhListenSocket.push_back(hListenSocket);
  
      if (addrBind.IsRoutable() && fDiscover)
          AddLocal(addrBind, LOCAL_BIND);
  
      return true;
  }
  
  void static Discover()
  {
      if (!fDiscover)
          return;
  
  #ifdef WIN32
      // Get local host IP
      char pszHostName[1000] = "";
      if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
      {
          vector<CNetAddr> vaddr;
          if (LookupHost(pszHostName, vaddr))
          {
              for (const CNetAddr &addr : vaddr)
              {
                  AddLocal(addr, LOCAL_IF);
              }
          }
      }
  #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("IPv4 %s: %s\n", 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("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString());
              }
          }
          freeifaddrs(myaddrs);
      }
  #endif
  
  }
  
  void StartNode(boost::thread_group& threadGroup)
  {
      if (semOutbound == NULL) {
          // initialize semaphore
          int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
          semOutbound = new CSemaphore(nMaxOutbound);
      }
  
      if (pnodeLocalHost == NULL)
          pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
  
      Discover();
  
      //
      // Start threads
      //
  
      // Send and receive from sockets, accept connections
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler));
  
      if (!GetBoolArg("-dnsseed", true))
          LogPrintf("DNS seeding disabled\n");
      else
          threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed));
  
      // Initiate outbound connections from -addnode
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections));
  
      // Initiate outbound connections
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections));
  
      // Process messages
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
  
      // Dump network addresses
      threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 360000));
  }
  
  bool StopNode()
  {
      LogPrintf("StopNode()\n");
      nTransactionsUpdated++;
      if (semOutbound)
          for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
              semOutbound->post();
      DumpAddresses();
      return true;
  }
  
  class CNetCleanup
  {
  public:
      CNetCleanup()
      {
      }
      ~CNetCleanup()
      {
          // Close sockets
          for (CNode* pnode : vNodes)
              if (pnode->hSocket != INVALID_SOCKET)
                  closesocket(pnode->hSocket);
          for (SOCKET hListenSocket : vhListenSocket)
              if (hListenSocket != INVALID_SOCKET)
                  if (closesocket(hListenSocket) == SOCKET_ERROR)
                      LogPrintf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
  
  #ifdef WIN32
          // Shutdown Windows Sockets
          WSACleanup();
  #endif
      }
  }
  instance_of_cnetcleanup;
  
  void RelayTransaction(const CTransaction& tx, const uint256& hash)
  {
      CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
      ss.reserve(10000);
      ss << tx;
      RelayTransaction(tx, hash, ss);
  }
  
  void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss)
  {
      CInv inv(MSG_TX, hash);
      {
          LOCK(cs_mapRelay);
          // Expire old relay messages
          while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())
          {
              mapRelay.erase(vRelayExpiration.front().second);
              vRelayExpiration.pop_front();
          }
  
          // Save original serialized message so newer versions are preserved
          mapRelay.insert(std::make_pair(inv, ss));
          vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
      }
  
      RelayInventory(inv);
  }
  
  void CNode::RecordBytesRecv(uint64_t bytes)
  {
      LOCK(cs_totalBytesRecv);
      nTotalBytesRecv += bytes;
  }
  
  void CNode::RecordBytesSent(uint64_t bytes)
  {
      LOCK(cs_totalBytesSent);
      nTotalBytesSent += bytes;
  }
  
  uint64_t CNode::GetTotalBytesRecv()
  {
      LOCK(cs_totalBytesRecv);
      return nTotalBytesRecv;
  }
  
  uint64_t CNode::GetTotalBytesSent()
  {
      LOCK(cs_totalBytesSent);
      return nTotalBytesSent;
  }
  