-// Copyright (c) 2009-2010 Satoshi Nakamoto\r
-// Distributed under the MIT/X11 software license, see the accompanying\r
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
-\r
-class CMessageHeader;\r
-class CAddress;\r
-class CInv;\r
-class CRequestTracker;\r
-class CNode;\r
-class CBlockIndex;\r
-\r
-\r
-\r
-static const unsigned short DEFAULT_PORT = htons(8333);\r
-static const unsigned int PUBLISH_HOPS = 5;\r
-enum\r
-{\r
- NODE_NETWORK = (1 << 0),\r
-};\r
-\r
-\r
-\r
-\r
-bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet);\r
-bool GetMyExternalIP(unsigned int& ipRet);\r
-bool AddAddress(CAddress addr, bool fCurrentlyOnline=true);\r
-void AddressCurrentlyConnected(const CAddress& addr);\r
-CNode* FindNode(unsigned int ip);\r
-CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0);\r
-void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);\r
-bool AnySubscribed(unsigned int nChannel);\r
-bool BindListenPort(string& strError=REF(string()));\r
-void StartNode(void* parg);\r
-bool StopNode();\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//\r
-// Message header\r
-// (4) message start\r
-// (12) command\r
-// (4) size\r
-\r
-// The message start string is designed to be unlikely to occur in normal data.\r
-// The characters are rarely used upper ascii, not valid as UTF-8, and produce\r
-// a large 4-byte int at any alignment.\r
-static const char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };\r
-\r
-class CMessageHeader\r
-{\r
-public:\r
- enum { COMMAND_SIZE=12 };\r
- char pchMessageStart[sizeof(::pchMessageStart)];\r
- char pchCommand[COMMAND_SIZE];\r
- unsigned int nMessageSize;\r
-\r
- CMessageHeader()\r
- {\r
- memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
- memset(pchCommand, 0, sizeof(pchCommand));\r
- pchCommand[1] = 1;\r
- nMessageSize = -1;\r
- }\r
-\r
- CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)\r
- {\r
- memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
- strncpy(pchCommand, pszCommand, COMMAND_SIZE);\r
- nMessageSize = nMessageSizeIn;\r
- }\r
-\r
- IMPLEMENT_SERIALIZE\r
- (\r
- READWRITE(FLATDATA(pchMessageStart));\r
- READWRITE(FLATDATA(pchCommand));\r
- READWRITE(nMessageSize);\r
- )\r
-\r
- string GetCommand()\r
- {\r
- if (pchCommand[COMMAND_SIZE-1] == 0)\r
- return string(pchCommand, pchCommand + strlen(pchCommand));\r
- else\r
- return string(pchCommand, pchCommand + COMMAND_SIZE);\r
- }\r
-\r
- bool IsValid()\r
- {\r
- // Check start string\r
- if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0)\r
- return false;\r
-\r
- // Check the command string for errors\r
- for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)\r
- {\r
- if (*p1 == 0)\r
- {\r
- // Must be all zeros after the first zero\r
- for (; p1 < pchCommand + COMMAND_SIZE; p1++)\r
- if (*p1 != 0)\r
- return false;\r
- }\r
- else if (*p1 < ' ' || *p1 > 0x7E)\r
- return false;\r
- }\r
-\r
- // Message size\r
- if (nMessageSize > 0x10000000)\r
- {\r
- printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize);\r
- return false;\r
- }\r
-\r
- return true;\r
- }\r
-};\r
-\r
-\r
-\r
-\r
-\r
-\r
-static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };\r
-\r
-class CAddress\r
-{\r
-public:\r
- uint64 nServices;\r
- unsigned char pchReserved[12];\r
- unsigned int ip;\r
- unsigned short port;\r
-\r
- // disk only\r
- unsigned int nTime;\r
-\r
- // memory only\r
- unsigned int nLastTry;\r
-\r
- CAddress()\r
- {\r
- Init();\r
- }\r
-\r
- CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=NODE_NETWORK)\r
- {\r
- Init();\r
- ip = ipIn;\r
- port = portIn;\r
- nServices = nServicesIn;\r
- }\r
-\r
- explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)\r
- {\r
- Init();\r
- ip = sockaddr.sin_addr.s_addr;\r
- port = sockaddr.sin_port;\r
- nServices = nServicesIn;\r
- }\r
-\r
- explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)\r
- {\r
- Init();\r
- SetAddress(pszIn);\r
- nServices = nServicesIn;\r
- }\r
-\r
- explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)\r
- {\r
- Init();\r
- SetAddress(strIn.c_str());\r
- nServices = nServicesIn;\r
- }\r
-\r
- void Init()\r
- {\r
- nServices = NODE_NETWORK;\r
- memcpy(pchReserved, pchIPv4, sizeof(pchReserved));\r
- ip = INADDR_NONE;\r
- port = DEFAULT_PORT;\r
- nTime = GetAdjustedTime();\r
- nLastTry = 0;\r
- }\r
-\r
- bool SetAddress(const char* pszIn)\r
- {\r
- ip = INADDR_NONE;\r
- port = DEFAULT_PORT;\r
- char psz[100];\r
- strlcpy(psz, pszIn, sizeof(psz));\r
- unsigned int a=0, b=0, c=0, d=0, e=0;\r
- if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)\r
- return false;\r
- char* pszPort = strchr(psz, ':');\r
- if (pszPort)\r
- {\r
- *pszPort++ = '\0';\r
- port = htons(atoi(pszPort));\r
- if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)\r
- port = htons(USHRT_MAX);\r
- }\r
- ip = inet_addr(psz);\r
- return IsValid();\r
- }\r
-\r
- bool SetAddress(string strIn)\r
- {\r
- return SetAddress(strIn.c_str());\r
- }\r
-\r
- IMPLEMENT_SERIALIZE\r
- (\r
- if (nType & SER_DISK)\r
- {\r
- READWRITE(nVersion);\r
- READWRITE(nTime);\r
- }\r
- READWRITE(nServices);\r
- READWRITE(FLATDATA(pchReserved)); // for IPv6\r
- READWRITE(ip);\r
- READWRITE(port);\r
- )\r
-\r
- friend inline bool operator==(const CAddress& a, const CAddress& b)\r
- {\r
- return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 &&\r
- a.ip == b.ip &&\r
- a.port == b.port);\r
- }\r
-\r
- friend inline bool operator!=(const CAddress& a, const CAddress& b)\r
- {\r
- return (!(a == b));\r
- }\r
-\r
- friend inline bool operator<(const CAddress& a, const CAddress& b)\r
- {\r
- int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved));\r
- if (ret < 0)\r
- return true;\r
- else if (ret == 0)\r
- {\r
- if (ntohl(a.ip) < ntohl(b.ip))\r
- return true;\r
- else if (a.ip == b.ip)\r
- return ntohs(a.port) < ntohs(b.port);\r
- }\r
- return false;\r
- }\r
-\r
- vector<unsigned char> GetKey() const\r
- {\r
- CDataStream ss;\r
- ss.reserve(18);\r
- ss << FLATDATA(pchReserved) << ip << port;\r
-\r
- #if defined(_MSC_VER) && _MSC_VER < 1300\r
- return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]);\r
- #else\r
- return vector<unsigned char>(ss.begin(), ss.end());\r
- #endif\r
- }\r
-\r
- struct sockaddr_in GetSockAddr() const\r
- {\r
- struct sockaddr_in sockaddr;\r
- memset(&sockaddr, 0, sizeof(sockaddr));\r
- sockaddr.sin_family = AF_INET;\r
- sockaddr.sin_addr.s_addr = ip;\r
- sockaddr.sin_port = port;\r
- return sockaddr;\r
- }\r
-\r
- bool IsIPv4() const\r
- {\r
- return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0);\r
- }\r
-\r
- bool IsRoutable() const\r
- {\r
- return !(GetByte(3) == 10 ||\r
- (GetByte(3) == 192 && GetByte(2) == 168) ||\r
- GetByte(3) == 127 ||\r
- GetByte(3) == 0 ||\r
- ip == 0 ||\r
- ip == INADDR_NONE);\r
- }\r
-\r
- bool IsValid() const\r
- {\r
- return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX));\r
- }\r
-\r
- unsigned char GetByte(int n) const\r
- {\r
- return ((unsigned char*)&ip)[3-n];\r
- }\r
-\r
- string ToStringIPPort() const\r
- {\r
- return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
- }\r
-\r
- string ToStringIP() const\r
- {\r
- return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));\r
- }\r
-\r
- string ToStringPort() const\r
- {\r
- return strprintf("%u", ntohs(port));\r
- }\r
-\r
- string ToStringLog() const\r
- {\r
- return "";\r
- }\r
-\r
- string ToString() const\r
- {\r
- return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
- }\r
-\r
- void print() const\r
- {\r
- printf("CAddress(%s)\n", ToString().c_str());\r
- }\r
-};\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-enum\r
-{\r
- MSG_TX = 1,\r
- MSG_BLOCK,\r
-};\r
-\r
-static const char* ppszTypeName[] =\r
-{\r
- "ERROR",\r
- "tx",\r
- "block",\r
-};\r
-\r
-class CInv\r
-{\r
-public:\r
- int type;\r
- uint256 hash;\r
-\r
- CInv()\r
- {\r
- type = 0;\r
- hash = 0;\r
- }\r
-\r
- CInv(int typeIn, const uint256& hashIn)\r
- {\r
- type = typeIn;\r
- hash = hashIn;\r
- }\r
-\r
- CInv(const string& strType, const uint256& hashIn)\r
- {\r
- int i;\r
- for (i = 1; i < ARRAYLEN(ppszTypeName); i++)\r
- {\r
- if (strType == ppszTypeName[i])\r
- {\r
- type = i;\r
- break;\r
- }\r
- }\r
- if (i == ARRAYLEN(ppszTypeName))\r
- throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str()));\r
- hash = hashIn;\r
- }\r
-\r
- IMPLEMENT_SERIALIZE\r
- (\r
- READWRITE(type);\r
- READWRITE(hash);\r
- )\r
-\r
- friend inline bool operator<(const CInv& a, const CInv& b)\r
- {\r
- return (a.type < b.type || (a.type == b.type && a.hash < b.hash));\r
- }\r
-\r
- bool IsKnownType() const\r
- {\r
- return (type >= 1 && type < ARRAYLEN(ppszTypeName));\r
- }\r
-\r
- const char* GetCommand() const\r
- {\r
- if (!IsKnownType())\r
- throw std::out_of_range(strprintf("CInv::GetCommand() : type=% unknown type", type));\r
- return ppszTypeName[type];\r
- }\r
-\r
- string ToString() const\r
- {\r
- return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,16).c_str());\r
- }\r
-\r
- void print() const\r
- {\r
- printf("CInv(%s)\n", ToString().c_str());\r
- }\r
-};\r
-\r
-\r
-\r
-\r
-\r
-class CRequestTracker\r
-{\r
-public:\r
- void (*fn)(void*, CDataStream&);\r
- void* param1;\r
-\r
- explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)\r
- {\r
- fn = fnIn;\r
- param1 = param1In;\r
- }\r
-\r
- bool IsNull()\r
- {\r
- return fn == NULL;\r
- }\r
-};\r
-\r
-\r
-\r
-\r
-\r
-extern bool fClient;\r
-extern uint64 nLocalServices;\r
-extern CAddress addrLocalHost;\r
-extern CNode* pnodeLocalHost;\r
-extern uint64 nLocalHostNonce;\r
-extern array<int, 10> vnThreadsRunning;\r
-extern SOCKET hListenSocket;\r
-extern int64 nThreadSocketHandlerHeartbeat;\r
-\r
-extern vector<CNode*> vNodes;\r
-extern CCriticalSection cs_vNodes;\r
-extern map<vector<unsigned char>, CAddress> mapAddresses;\r
-extern CCriticalSection cs_mapAddresses;\r
-extern map<CInv, CDataStream> mapRelay;\r
-extern deque<pair<int64, CInv> > vRelayExpiration;\r
-extern CCriticalSection cs_mapRelay;\r
-extern map<CInv, int64> mapAlreadyAskedFor;\r
-\r
-// Settings\r
-extern int fUseProxy;\r
-extern CAddress addrProxy;\r
-\r
-\r
-\r
-\r
-\r
-class CNode\r
-{\r
-public:\r
- // socket\r
- uint64 nServices;\r
- SOCKET hSocket;\r
- CDataStream vSend;\r
- CDataStream vRecv;\r
- CCriticalSection cs_vSend;\r
- CCriticalSection cs_vRecv;\r
- int64 nLastSend;\r
- int64 nLastRecv;\r
- int64 nLastSendEmpty;\r
- int64 nTimeConnected;\r
- unsigned int nPushPos;\r
- CAddress addr;\r
- int nVersion;\r
- bool fClient;\r
- bool fInbound;\r
- bool fNetworkNode;\r
- bool fSuccessfullyConnected;\r
- bool fDisconnect;\r
-protected:\r
- int nRefCount;\r
-public:\r
- int64 nReleaseTime;\r
- map<uint256, CRequestTracker> mapRequests;\r
- CCriticalSection cs_mapRequests;\r
- uint256 hashContinue;\r
- CBlockIndex* pindexLastGetBlocksBegin;\r
- uint256 hashLastGetBlocksEnd;\r
-\r
- // flood\r
- vector<CAddress> vAddrToSend;\r
- set<CAddress> setAddrKnown;\r
- bool fGetAddr;\r
-\r
- // inventory based relay\r
- set<CInv> setInventoryKnown;\r
- vector<CInv> vInventoryToSend;\r
- CCriticalSection cs_inventory;\r
- multimap<int64, CInv> mapAskFor;\r
- int64 nLastSentTxInv;\r
-\r
- // publish and subscription\r
- vector<char> vfSubscribe;\r
-\r
-\r
- CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)\r
- {\r
- nServices = 0;\r
- hSocket = hSocketIn;\r
- vSend.SetType(SER_NETWORK);\r
- vRecv.SetType(SER_NETWORK);\r
- nLastSend = 0;\r
- nLastRecv = 0;\r
- nLastSendEmpty = GetTime();\r
- nTimeConnected = GetTime();\r
- nPushPos = -1;\r
- addr = addrIn;\r
- nVersion = 0;\r
- fClient = false; // set by version message\r
- fInbound = fInboundIn;\r
- fNetworkNode = false;\r
- fSuccessfullyConnected = false;\r
- fDisconnect = false;\r
- nRefCount = 0;\r
- nReleaseTime = 0;\r
- hashContinue = 0;\r
- pindexLastGetBlocksBegin = 0;\r
- hashLastGetBlocksEnd = 0;\r
- fGetAddr = false;\r
- vfSubscribe.assign(256, false);\r
-\r
- // Push a version message\r
- /// when NTP implemented, change to just nTime = GetAdjustedTime()\r
- int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());\r
- CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);\r
- CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);\r
- RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));\r
- PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, string(pszSubVer));\r
- }\r
-\r
- ~CNode()\r
- {\r
- if (hSocket != INVALID_SOCKET)\r
- {\r
- closesocket(hSocket);\r
- hSocket = INVALID_SOCKET;\r
- }\r
- }\r
-\r
-private:\r
- CNode(const CNode&);\r
- void operator=(const CNode&);\r
-public:\r
-\r
-\r
- int GetRefCount()\r
- {\r
- return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);\r
- }\r
-\r
- CNode* AddRef(int64 nTimeout=0)\r
- {\r
- if (nTimeout != 0)\r
- nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);\r
- else\r
- nRefCount++;\r
- return this;\r
- }\r
-\r
- void Release()\r
- {\r
- nRefCount--;\r
- }\r
-\r
-\r
-\r
- void AddAddressKnown(const CAddress& addr)\r
- {\r
- setAddrKnown.insert(addr);\r
- }\r
-\r
- void PushAddress(const CAddress& addr)\r
- {\r
- // Known checking here is only to save space from duplicates.\r
- // SendMessages will filter it again for knowns that were added\r
- // after addresses were pushed.\r
- if (!setAddrKnown.count(addr))\r
- vAddrToSend.push_back(addr);\r
- }\r
-\r
-\r
- void AddInventoryKnown(const CInv& inv)\r
- {\r
- CRITICAL_BLOCK(cs_inventory)\r
- setInventoryKnown.insert(inv);\r
- }\r
-\r
- void PushInventory(const CInv& inv)\r
- {\r
- CRITICAL_BLOCK(cs_inventory)\r
- if (!setInventoryKnown.count(inv))\r
- vInventoryToSend.push_back(inv);\r
- }\r
-\r
- void AskFor(const CInv& inv)\r
- {\r
- // We're using mapAskFor as a priority queue,\r
- // the key is the earliest time the request can be sent\r
- int64& nRequestTime = mapAlreadyAskedFor[inv];\r
- printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime);\r
-\r
- // Make sure not to reuse time indexes to keep things in the same order\r
- int64 nNow = (GetTime() - 1) * 1000000;\r
- static int64 nLastTime;\r
- nLastTime = nNow = max(nNow, ++nLastTime);\r
-\r
- // Each retry is 2 minutes after the last\r
- nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow);\r
- mapAskFor.insert(make_pair(nRequestTime, inv));\r
- }\r
-\r
-\r
-\r
- void BeginMessage(const char* pszCommand)\r
- {\r
- cs_vSend.Enter();\r
- if (nPushPos != -1)\r
- AbortMessage();\r
- nPushPos = vSend.size();\r
- vSend << CMessageHeader(pszCommand, 0);\r
- if (fDebug)\r
- printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
- printf("sending: %s ", pszCommand);\r
- }\r
-\r
- void AbortMessage()\r
- {\r
- if (nPushPos == -1)\r
- return;\r
- vSend.resize(nPushPos);\r
- nPushPos = -1;\r
- cs_vSend.Leave();\r
- printf("(aborted)\n");\r
- }\r
-\r
- void EndMessage()\r
- {\r
- if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)\r
- {\r
- printf("dropmessages DROPPING SEND MESSAGE\n");\r
- AbortMessage();\r
- return;\r
- }\r
-\r
- if (nPushPos == -1)\r
- return;\r
-\r
- // Patch in the size\r
- unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
- memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));\r
-\r
- printf("(%d bytes) ", nSize);\r
- printf("\n");\r
-\r
- nPushPos = -1;\r
- cs_vSend.Leave();\r
- }\r
-\r
- void EndMessageAbortIfEmpty()\r
- {\r
- if (nPushPos == -1)\r
- return;\r
- int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
- if (nSize > 0)\r
- EndMessage();\r
- else\r
- AbortMessage();\r
- }\r
-\r
- const char* GetMessageCommand() const\r
- {\r
- if (nPushPos == -1)\r
- return "";\r
- return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand);\r
- }\r
-\r
-\r
-\r
-\r
- void PushMessage(const char* pszCommand)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1>\r
- void PushMessage(const char* pszCommand, const T1& a1)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4, typename T5>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4 << a5;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4 << a5 << a6;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>\r
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9)\r
- {\r
- try\r
- {\r
- BeginMessage(pszCommand);\r
- vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;\r
- EndMessage();\r
- }\r
- catch (...)\r
- {\r
- AbortMessage();\r
- throw;\r
- }\r
- }\r
-\r
-\r
- void PushRequest(const char* pszCommand,\r
- void (*fn)(void*, CDataStream&), void* param1)\r
- {\r
- uint256 hashReply;\r
- RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
-\r
- CRITICAL_BLOCK(cs_mapRequests)\r
- mapRequests[hashReply] = CRequestTracker(fn, param1);\r
-\r
- PushMessage(pszCommand, hashReply);\r
- }\r
-\r
- template<typename T1>\r
- void PushRequest(const char* pszCommand, const T1& a1,\r
- void (*fn)(void*, CDataStream&), void* param1)\r
- {\r
- uint256 hashReply;\r
- RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
-\r
- CRITICAL_BLOCK(cs_mapRequests)\r
- mapRequests[hashReply] = CRequestTracker(fn, param1);\r
-\r
- PushMessage(pszCommand, hashReply, a1);\r
- }\r
-\r
- template<typename T1, typename T2>\r
- void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,\r
- void (*fn)(void*, CDataStream&), void* param1)\r
- {\r
- uint256 hashReply;\r
- RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
-\r
- CRITICAL_BLOCK(cs_mapRequests)\r
- mapRequests[hashReply] = CRequestTracker(fn, param1);\r
-\r
- PushMessage(pszCommand, hashReply, a1, a2);\r
- }\r
-\r
-\r
-\r
- void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);\r
- bool IsSubscribed(unsigned int nChannel);\r
- void Subscribe(unsigned int nChannel, unsigned int nHops=0);\r
- void CancelSubscribe(unsigned int nChannel);\r
- void CloseSocketDisconnect();\r
- void Cleanup();\r
-};\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-inline void RelayInventory(const CInv& inv)\r
-{\r
- // Put on lists to offer to the other nodes\r
- CRITICAL_BLOCK(cs_vNodes)\r
- foreach(CNode* pnode, vNodes)\r
- pnode->PushInventory(inv);\r
-}\r
-\r
-template<typename T>\r
-void RelayMessage(const CInv& inv, const T& a)\r
-{\r
- CDataStream ss(SER_NETWORK);\r
- ss.reserve(10000);\r
- ss << a;\r
- RelayMessage(inv, ss);\r
-}\r
-\r
-template<>\r
-inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)\r
-{\r
- CRITICAL_BLOCK(cs_mapRelay)\r
- {\r
- // Expire old relay messages\r
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())\r
- {\r
- mapRelay.erase(vRelayExpiration.front().second);\r
- vRelayExpiration.pop_front();\r
- }\r
-\r
- // Save original serialized message so newer versions are preserved\r
- mapRelay[inv] = ss;\r
- vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));\r
- }\r
-\r
- RelayInventory(inv);\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//\r
-// Templates for the publish and subscription system.\r
-// The object being published as T& obj needs to have:\r
-// a set<unsigned int> setSources member\r
-// specializations of AdvertInsert and AdvertErase\r
-// Currently implemented for CTable and CProduct.\r
-//\r
-\r
-template<typename T>\r
-void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
-{\r
- // Add to sources\r
- obj.setSources.insert(pfrom->addr.ip);\r
-\r
- if (!AdvertInsert(obj))\r
- return;\r
-\r
- // Relay\r
- CRITICAL_BLOCK(cs_vNodes)\r
- foreach(CNode* pnode, vNodes)\r
- if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
- pnode->PushMessage("publish", nChannel, nHops, obj);\r
-}\r
-\r
-template<typename T>\r
-void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
-{\r
- uint256 hash = obj.GetHash();\r
-\r
- CRITICAL_BLOCK(cs_vNodes)\r
- foreach(CNode* pnode, vNodes)\r
- if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
- pnode->PushMessage("pub-cancel", nChannel, nHops, hash);\r
-\r
- AdvertErase(obj);\r
-}\r
-\r
-template<typename T>\r
-void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
-{\r
- // Remove a source\r
- obj.setSources.erase(pfrom->addr.ip);\r
-\r
- // If no longer supported by any sources, cancel it\r
- if (obj.setSources.empty())\r
- AdvertStopPublish(pfrom, nChannel, nHops, obj);\r
-}\r
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+class CMessageHeader;
+class CAddress;
+class CInv;
+class CRequestTracker;
+class CNode;
+class CBlockIndex;
+extern int nBestHeight;
+
+
+
+inline unsigned short GetDefaultPort() { return fTestNet ? htons(18333) : htons(8333); }
+static const unsigned int PUBLISH_HOPS = 5;
+enum
+{
+ NODE_NETWORK = (1 << 0),
+};
+
+
+
+
+bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet);
+bool GetMyExternalIP(unsigned int& ipRet);
+bool AddAddress(CAddress addr, int64 nTimePenalty=0);
+void AddressCurrentlyConnected(const CAddress& addr);
+CNode* FindNode(unsigned int ip);
+CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0);
+void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);
+bool AnySubscribed(unsigned int nChannel);
+void DNSAddressSeed();
+bool BindListenPort(string& strError=REF(string()));
+void StartNode(void* parg);
+bool StopNode();
+
+
+
+
+
+
+
+
+//
+// Message header
+// (4) message start
+// (12) command
+// (4) size
+// (4) checksum
+
+extern char pchMessageStart[4];
+
+class CMessageHeader
+{
+public:
+ enum { COMMAND_SIZE=12 };
+ char pchMessageStart[sizeof(::pchMessageStart)];
+ char pchCommand[COMMAND_SIZE];
+ unsigned int nMessageSize;
+ unsigned int nChecksum;
+
+ CMessageHeader()
+ {
+ memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));
+ memset(pchCommand, 0, sizeof(pchCommand));
+ pchCommand[1] = 1;
+ nMessageSize = -1;
+ nChecksum = 0;
+ }
+
+ CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)
+ {
+ memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));
+ strncpy(pchCommand, pszCommand, COMMAND_SIZE);
+ nMessageSize = nMessageSizeIn;
+ nChecksum = 0;
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(FLATDATA(pchMessageStart));
+ READWRITE(FLATDATA(pchCommand));
+ READWRITE(nMessageSize);
+ if (nVersion >= 209)
+ READWRITE(nChecksum);
+ )
+
+ string GetCommand()
+ {
+ if (pchCommand[COMMAND_SIZE-1] == 0)
+ return string(pchCommand, pchCommand + strlen(pchCommand));
+ else
+ return string(pchCommand, pchCommand + COMMAND_SIZE);
+ }
+
+ bool IsValid()
+ {
+ // Check start string
+ if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0)
+ return false;
+
+ // Check the command string for errors
+ for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)
+ {
+ if (*p1 == 0)
+ {
+ // Must be all zeros after the first zero
+ for (; p1 < pchCommand + COMMAND_SIZE; p1++)
+ if (*p1 != 0)
+ return false;
+ }
+ else if (*p1 < ' ' || *p1 > 0x7E)
+ return false;
+ }
+
+ // Message size
+ if (nMessageSize > MAX_SIZE)
+ {
+ printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize);
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+
+
+
+
+static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+
+class CAddress
+{
+public:
+ uint64 nServices;
+ unsigned char pchReserved[12];
+ unsigned int ip;
+ unsigned short port;
+
+ // disk and network only
+ unsigned int nTime;
+
+ // memory only
+ unsigned int nLastTry;
+
+ CAddress()
+ {
+ Init();
+ }
+
+ CAddress(unsigned int ipIn, unsigned short portIn=0, uint64 nServicesIn=NODE_NETWORK)
+ {
+ Init();
+ ip = ipIn;
+ port = (portIn == 0 ? GetDefaultPort() : portIn);
+ nServices = nServicesIn;
+ }
+
+ explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)
+ {
+ Init();
+ ip = sockaddr.sin_addr.s_addr;
+ port = sockaddr.sin_port;
+ nServices = nServicesIn;
+ }
+
+ explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)
+ {
+ Init();
+ SetAddress(pszIn);
+ nServices = nServicesIn;
+ }
+
+ explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)
+ {
+ Init();
+ SetAddress(strIn.c_str());
+ nServices = nServicesIn;
+ }
+
+ void Init()
+ {
+ nServices = NODE_NETWORK;
+ memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
+ ip = INADDR_NONE;
+ port = GetDefaultPort();
+ nTime = 100000000;
+ nLastTry = 0;
+ }
+
+ bool SetAddress(const char* pszIn)
+ {
+ ip = INADDR_NONE;
+ port = GetDefaultPort();
+ char psz[100];
+ strlcpy(psz, pszIn, sizeof(psz));
+ unsigned int a=0, b=0, c=0, d=0, e=0;
+ if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)
+ return false;
+ char* pszPort = strchr(psz, ':');
+ if (pszPort)
+ {
+ *pszPort++ = '\0';
+ port = htons(atoi(pszPort));
+ if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)
+ port = htons(USHRT_MAX);
+ }
+ ip = inet_addr(psz);
+ return IsValid();
+ }
+
+ bool SetAddress(string strIn)
+ {
+ return SetAddress(strIn.c_str());
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ if (fRead)
+ const_cast<CAddress*>(this)->Init();
+ if (nType & SER_DISK)
+ READWRITE(nVersion);
+ if ((nType & SER_DISK) || (nVersion >= 31402 && !(nType & SER_GETHASH)))
+ READWRITE(nTime);
+ READWRITE(nServices);
+ READWRITE(FLATDATA(pchReserved)); // for IPv6
+ READWRITE(ip);
+ READWRITE(port);
+ )
+
+ friend inline bool operator==(const CAddress& a, const CAddress& b)
+ {
+ return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 &&
+ a.ip == b.ip &&
+ a.port == b.port);
+ }
+
+ friend inline bool operator!=(const CAddress& a, const CAddress& b)
+ {
+ return (!(a == b));
+ }
+
+ friend inline bool operator<(const CAddress& a, const CAddress& b)
+ {
+ int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved));
+ if (ret < 0)
+ return true;
+ else if (ret == 0)
+ {
+ if (ntohl(a.ip) < ntohl(b.ip))
+ return true;
+ else if (a.ip == b.ip)
+ return ntohs(a.port) < ntohs(b.port);
+ }
+ return false;
+ }
+
+ vector<unsigned char> GetKey() const
+ {
+ CDataStream ss;
+ ss.reserve(18);
+ ss << FLATDATA(pchReserved) << ip << port;
+
+ #if defined(_MSC_VER) && _MSC_VER < 1300
+ return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]);
+ #else
+ return vector<unsigned char>(ss.begin(), ss.end());
+ #endif
+ }
+
+ struct sockaddr_in GetSockAddr() const
+ {
+ struct sockaddr_in sockaddr;
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr.s_addr = ip;
+ sockaddr.sin_port = port;
+ return sockaddr;
+ }
+
+ bool IsIPv4() const
+ {
+ return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0);
+ }
+
+ bool IsRoutable() const
+ {
+ return IsValid() &&
+ !(GetByte(3) == 10 ||
+ (GetByte(3) == 192 && GetByte(2) == 168) ||
+ GetByte(3) == 127 ||
+ GetByte(3) == 0);
+ }
+
+ bool IsValid() const
+ {
+ // Clean up 3-byte shifted addresses caused by garbage in size field
+ // of addr messages from versions before 0.2.9 checksum.
+ // Two consecutive addr messages look like this:
+ // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
+ // so if the first length field is garbled, it reads the second batch
+ // of addr misaligned by 3 bytes.
+ if (memcmp(pchReserved, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
+ return false;
+
+ return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX));
+ }
+
+ unsigned char GetByte(int n) const
+ {
+ return ((unsigned char*)&ip)[3-n];
+ }
+
+ string ToStringIPPort() const
+ {
+ return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));
+ }
+
+ string ToStringIP() const
+ {
+ return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
+ }
+
+ string ToStringPort() const
+ {
+ return strprintf("%u", ntohs(port));
+ }
+
+ string ToStringLog() const
+ {
+ return "";
+ }
+
+ string ToString() const
+ {
+ return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));
+ }
+
+ void print() const
+ {
+ printf("CAddress(%s)\n", ToString().c_str());
+ }
+};
+
+
+
+
+
+
+
+enum
+{
+ MSG_TX = 1,
+ MSG_BLOCK,
+};
+
+static const char* ppszTypeName[] =
+{
+ "ERROR",
+ "tx",
+ "block",
+};
+
+class CInv
+{
+public:
+ int type;
+ uint256 hash;
+
+ CInv()
+ {
+ type = 0;
+ hash = 0;
+ }
+
+ CInv(int typeIn, const uint256& hashIn)
+ {
+ type = typeIn;
+ hash = hashIn;
+ }
+
+ CInv(const string& strType, const uint256& hashIn)
+ {
+ int i;
+ for (i = 1; i < ARRAYLEN(ppszTypeName); i++)
+ {
+ if (strType == ppszTypeName[i])
+ {
+ type = i;
+ break;
+ }
+ }
+ if (i == ARRAYLEN(ppszTypeName))
+ throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str()));
+ hash = hashIn;
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(type);
+ READWRITE(hash);
+ )
+
+ friend inline bool operator<(const CInv& a, const CInv& b)
+ {
+ return (a.type < b.type || (a.type == b.type && a.hash < b.hash));
+ }
+
+ bool IsKnownType() const
+ {
+ return (type >= 1 && type < ARRAYLEN(ppszTypeName));
+ }
+
+ const char* GetCommand() const
+ {
+ if (!IsKnownType())
+ throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type));
+ return ppszTypeName[type];
+ }
+
+ string ToString() const
+ {
+ return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str());
+ }
+
+ void print() const
+ {
+ printf("CInv(%s)\n", ToString().c_str());
+ }
+};
+
+
+
+
+
+class CRequestTracker
+{
+public:
+ void (*fn)(void*, CDataStream&);
+ void* param1;
+
+ explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)
+ {
+ fn = fnIn;
+ param1 = param1In;
+ }
+
+ bool IsNull()
+ {
+ return fn == NULL;
+ }
+};
+
+
+
+
+
+extern bool fClient;
+extern uint64 nLocalServices;
+extern CAddress addrLocalHost;
+extern CNode* pnodeLocalHost;
+extern uint64 nLocalHostNonce;
+extern array<int, 10> vnThreadsRunning;
+extern SOCKET hListenSocket;
+
+extern vector<CNode*> vNodes;
+extern CCriticalSection cs_vNodes;
+extern map<vector<unsigned char>, CAddress> mapAddresses;
+extern CCriticalSection cs_mapAddresses;
+extern map<CInv, CDataStream> mapRelay;
+extern deque<pair<int64, CInv> > vRelayExpiration;
+extern CCriticalSection cs_mapRelay;
+extern map<CInv, int64> mapAlreadyAskedFor;
+
+// Settings
+extern int fUseProxy;
+extern CAddress addrProxy;
+
+
+
+
+
+
+class CNode
+{
+public:
+ // socket
+ uint64 nServices;
+ SOCKET hSocket;
+ CDataStream vSend;
+ CDataStream vRecv;
+ CCriticalSection cs_vSend;
+ CCriticalSection cs_vRecv;
+ int64 nLastSend;
+ int64 nLastRecv;
+ int64 nLastSendEmpty;
+ int64 nTimeConnected;
+ unsigned int nHeaderStart;
+ unsigned int nMessageStart;
+ CAddress addr;
+ int nVersion;
+ string strSubVer;
+ bool fClient;
+ bool fInbound;
+ bool fNetworkNode;
+ bool fSuccessfullyConnected;
+ bool fDisconnect;
+protected:
+ int nRefCount;
+public:
+ int64 nReleaseTime;
+ map<uint256, CRequestTracker> mapRequests;
+ CCriticalSection cs_mapRequests;
+ uint256 hashContinue;
+ CBlockIndex* pindexLastGetBlocksBegin;
+ uint256 hashLastGetBlocksEnd;
+ int nStartingHeight;
+
+ // flood relay
+ vector<CAddress> vAddrToSend;
+ set<CAddress> setAddrKnown;
+ bool fGetAddr;
+ set<uint256> setKnown;
+
+ // inventory based relay
+ set<CInv> setInventoryKnown;
+ vector<CInv> vInventoryToSend;
+ CCriticalSection cs_inventory;
+ multimap<int64, CInv> mapAskFor;
+
+ // publish and subscription
+ vector<char> vfSubscribe;
+
+
+ CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)
+ {
+ nServices = 0;
+ hSocket = hSocketIn;
+ vSend.SetType(SER_NETWORK);
+ vSend.SetVersion(0);
+ vRecv.SetType(SER_NETWORK);
+ vRecv.SetVersion(0);
+ // Version 0.2 obsoletes 20 Feb 2012
+ if (GetTime() > 1329696000)
+ {
+ vSend.SetVersion(209);
+ vRecv.SetVersion(209);
+ }
+ nLastSend = 0;
+ nLastRecv = 0;
+ nLastSendEmpty = GetTime();
+ nTimeConnected = GetTime();
+ nHeaderStart = -1;
+ nMessageStart = -1;
+ addr = addrIn;
+ nVersion = 0;
+ strSubVer = "";
+ fClient = false; // set by version message
+ fInbound = fInboundIn;
+ fNetworkNode = false;
+ fSuccessfullyConnected = false;
+ fDisconnect = false;
+ nRefCount = 0;
+ nReleaseTime = 0;
+ hashContinue = 0;
+ pindexLastGetBlocksBegin = 0;
+ hashLastGetBlocksEnd = 0;
+ nStartingHeight = -1;
+ fGetAddr = false;
+ vfSubscribe.assign(256, false);
+
+ // Be shy and don't send version until we hear
+ if (!fInbound)
+ PushVersion();
+ }
+
+ ~CNode()
+ {
+ if (hSocket != INVALID_SOCKET)
+ {
+ closesocket(hSocket);
+ hSocket = INVALID_SOCKET;
+ }
+ }
+
+private:
+ CNode(const CNode&);
+ void operator=(const CNode&);
+public:
+
+
+ int GetRefCount()
+ {
+ return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);
+ }
+
+ CNode* AddRef(int64 nTimeout=0)
+ {
+ if (nTimeout != 0)
+ nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);
+ else
+ nRefCount++;
+ return this;
+ }
+
+ void Release()
+ {
+ nRefCount--;
+ }
+
+
+
+ void AddAddressKnown(const CAddress& addr)
+ {
+ setAddrKnown.insert(addr);
+ }
+
+ void PushAddress(const CAddress& addr)
+ {
+ // Known checking here is only to save space from duplicates.
+ // SendMessages will filter it again for knowns that were added
+ // after addresses were pushed.
+ if (addr.IsValid() && !setAddrKnown.count(addr))
+ vAddrToSend.push_back(addr);
+ }
+
+
+ void AddInventoryKnown(const CInv& inv)
+ {
+ CRITICAL_BLOCK(cs_inventory)
+ setInventoryKnown.insert(inv);
+ }
+
+ void PushInventory(const CInv& inv)
+ {
+ CRITICAL_BLOCK(cs_inventory)
+ if (!setInventoryKnown.count(inv))
+ vInventoryToSend.push_back(inv);
+ }
+
+ void AskFor(const CInv& inv)
+ {
+ // We're using mapAskFor as a priority queue,
+ // the key is the earliest time the request can be sent
+ int64& nRequestTime = mapAlreadyAskedFor[inv];
+ printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime);
+
+ // Make sure not to reuse time indexes to keep things in the same order
+ int64 nNow = (GetTime() - 1) * 1000000;
+ static int64 nLastTime;
+ nLastTime = nNow = max(nNow, ++nLastTime);
+
+ // Each retry is 2 minutes after the last
+ nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow);
+ mapAskFor.insert(make_pair(nRequestTime, inv));
+ }
+
+
+
+ void BeginMessage(const char* pszCommand)
+ {
+ cs_vSend.Enter();
+ if (nHeaderStart != -1)
+ AbortMessage();
+ nHeaderStart = vSend.size();
+ vSend << CMessageHeader(pszCommand, 0);
+ nMessageStart = vSend.size();
+ if (fDebug)
+ printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
+ printf("sending: %s ", pszCommand);
+ }
+
+ void AbortMessage()
+ {
+ if (nHeaderStart == -1)
+ return;
+ vSend.resize(nHeaderStart);
+ nHeaderStart = -1;
+ nMessageStart = -1;
+ cs_vSend.Leave();
+ printf("(aborted)\n");
+ }
+
+ void EndMessage()
+ {
+ if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
+ {
+ printf("dropmessages DROPPING SEND MESSAGE\n");
+ AbortMessage();
+ return;
+ }
+
+ if (nHeaderStart == -1)
+ return;
+
+ // Set the size
+ unsigned int nSize = vSend.size() - nMessageStart;
+ memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));
+
+ // Set the checksum
+ if (vSend.GetVersion() >= 209)
+ {
+ uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
+ unsigned int nChecksum = 0;
+ memcpy(&nChecksum, &hash, sizeof(nChecksum));
+ assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum));
+ memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum));
+ }
+
+ printf("(%d bytes) ", nSize);
+ printf("\n");
+
+ nHeaderStart = -1;
+ nMessageStart = -1;
+ cs_vSend.Leave();
+ }
+
+ void EndMessageAbortIfEmpty()
+ {
+ if (nHeaderStart == -1)
+ return;
+ int nSize = vSend.size() - nMessageStart;
+ if (nSize > 0)
+ EndMessage();
+ else
+ AbortMessage();
+ }
+
+
+
+ void PushVersion()
+ {
+ /// when NTP implemented, change to just nTime = GetAdjustedTime()
+ int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());
+ CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);
+ CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);
+ RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
+ PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe,
+ nLocalHostNonce, string(pszSubVer), nBestHeight);
+ }
+
+
+
+
+ void PushMessage(const char* pszCommand)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1>
+ void PushMessage(const char* pszCommand, const T1& a1)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4, typename T5>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4 << a5;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4 << a5 << a6;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+ template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
+ void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9)
+ {
+ try
+ {
+ BeginMessage(pszCommand);
+ vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;
+ EndMessage();
+ }
+ catch (...)
+ {
+ AbortMessage();
+ throw;
+ }
+ }
+
+
+ void PushRequest(const char* pszCommand,
+ void (*fn)(void*, CDataStream&), void* param1)
+ {
+ uint256 hashReply;
+ RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
+
+ CRITICAL_BLOCK(cs_mapRequests)
+ mapRequests[hashReply] = CRequestTracker(fn, param1);
+
+ PushMessage(pszCommand, hashReply);
+ }
+
+ template<typename T1>
+ void PushRequest(const char* pszCommand, const T1& a1,
+ void (*fn)(void*, CDataStream&), void* param1)
+ {
+ uint256 hashReply;
+ RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
+
+ CRITICAL_BLOCK(cs_mapRequests)
+ mapRequests[hashReply] = CRequestTracker(fn, param1);
+
+ PushMessage(pszCommand, hashReply, a1);
+ }
+
+ template<typename T1, typename T2>
+ void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,
+ void (*fn)(void*, CDataStream&), void* param1)
+ {
+ uint256 hashReply;
+ RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
+
+ CRITICAL_BLOCK(cs_mapRequests)
+ mapRequests[hashReply] = CRequestTracker(fn, param1);
+
+ PushMessage(pszCommand, hashReply, a1, a2);
+ }
+
+
+
+ void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);
+ bool IsSubscribed(unsigned int nChannel);
+ void Subscribe(unsigned int nChannel, unsigned int nHops=0);
+ void CancelSubscribe(unsigned int nChannel);
+ void CloseSocketDisconnect();
+ void Cleanup();
+};
+
+
+
+
+
+
+
+
+
+
+inline void RelayInventory(const CInv& inv)
+{
+ // Put on lists to offer to the other nodes
+ CRITICAL_BLOCK(cs_vNodes)
+ foreach(CNode* pnode, vNodes)
+ pnode->PushInventory(inv);
+}
+
+template<typename T>
+void RelayMessage(const CInv& inv, const T& a)
+{
+ CDataStream ss(SER_NETWORK);
+ ss.reserve(10000);
+ ss << a;
+ RelayMessage(inv, ss);
+}
+
+template<>
+inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)
+{
+ CRITICAL_BLOCK(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[inv] = ss;
+ vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));
+ }
+
+ RelayInventory(inv);
+}
+
+
+
+
+
+
+
+
+//
+// Templates for the publish and subscription system.
+// The object being published as T& obj needs to have:
+// a set<unsigned int> setSources member
+// specializations of AdvertInsert and AdvertErase
+// Currently implemented for CTable and CProduct.
+//
+
+template<typename T>
+void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
+{
+ // Add to sources
+ obj.setSources.insert(pfrom->addr.ip);
+
+ if (!AdvertInsert(obj))
+ return;
+
+ // Relay
+ CRITICAL_BLOCK(cs_vNodes)
+ foreach(CNode* pnode, vNodes)
+ if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))
+ pnode->PushMessage("publish", nChannel, nHops, obj);
+}
+
+template<typename T>
+void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
+{
+ uint256 hash = obj.GetHash();
+
+ CRITICAL_BLOCK(cs_vNodes)
+ foreach(CNode* pnode, vNodes)
+ if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))
+ pnode->PushMessage("pub-cancel", nChannel, nHops, hash);
+
+ AdvertErase(obj);
+}
+
+template<typename T>
+void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
+{
+ // Remove a source
+ obj.setSources.erase(pfrom->addr.ip);
+
+ // If no longer supported by any sources, cancel it
+ if (obj.setSources.empty())
+ AdvertStopPublish(pfrom, nChannel, nHops, obj);
+}