retry IRC if name in use,
[novacoin.git] / net.h
1 // Copyright (c) 2009 Satoshi Nakamoto\r
2 // Distributed under the MIT/X11 software license, see the accompanying\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
4 \r
5 class CMessageHeader;\r
6 class CAddress;\r
7 class CInv;\r
8 class CRequestTracker;\r
9 class CNode;\r
10 \r
11 \r
12 \r
13 static const unsigned short DEFAULT_PORT = htons(8333);\r
14 static const unsigned int PUBLISH_HOPS = 5;\r
15 enum\r
16 {\r
17     NODE_NETWORK = (1 << 0),\r
18 };\r
19 \r
20 \r
21 \r
22 \r
23 bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet);\r
24 bool GetMyExternalIP(unsigned int& ipRet);\r
25 bool AddAddress(CAddrDB& addrdb, CAddress addr, bool fCurrentlyOnline=true);\r
26 void AddressCurrentlyConnected(const CAddress& addr);\r
27 CNode* FindNode(unsigned int ip);\r
28 CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0);\r
29 void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);\r
30 bool AnySubscribed(unsigned int nChannel);\r
31 bool BindListenPort(string& strError=REF(string()));\r
32 void StartNode(void* parg);\r
33 bool StopNode();\r
34 \r
35 \r
36 \r
37 \r
38 \r
39 \r
40 \r
41 \r
42 //\r
43 // Message header\r
44 //  (4) message start\r
45 //  (12) command\r
46 //  (4) size\r
47 \r
48 // The message start string is designed to be unlikely to occur in normal data.\r
49 // The characters are rarely used upper ascii, not valid as UTF-8, and produce\r
50 // a large 4-byte int at any alignment.\r
51 static const char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };\r
52 \r
53 class CMessageHeader\r
54 {\r
55 public:\r
56     enum { COMMAND_SIZE=12 };\r
57     char pchMessageStart[sizeof(::pchMessageStart)];\r
58     char pchCommand[COMMAND_SIZE];\r
59     unsigned int nMessageSize;\r
60 \r
61     CMessageHeader()\r
62     {\r
63         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
64         memset(pchCommand, 0, sizeof(pchCommand));\r
65         pchCommand[1] = 1;\r
66         nMessageSize = -1;\r
67     }\r
68 \r
69     CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)\r
70     {\r
71         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
72         strncpy(pchCommand, pszCommand, COMMAND_SIZE);\r
73         nMessageSize = nMessageSizeIn;\r
74     }\r
75 \r
76     IMPLEMENT_SERIALIZE\r
77     (\r
78         READWRITE(FLATDATA(pchMessageStart));\r
79         READWRITE(FLATDATA(pchCommand));\r
80         READWRITE(nMessageSize);\r
81     )\r
82 \r
83     string GetCommand()\r
84     {\r
85         if (pchCommand[COMMAND_SIZE-1] == 0)\r
86             return string(pchCommand, pchCommand + strlen(pchCommand));\r
87         else\r
88             return string(pchCommand, pchCommand + COMMAND_SIZE);\r
89     }\r
90 \r
91     bool IsValid()\r
92     {\r
93         // Check start string\r
94         if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0)\r
95             return false;\r
96 \r
97         // Check the command string for errors\r
98         for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)\r
99         {\r
100             if (*p1 == 0)\r
101             {\r
102                 // Must be all zeros after the first zero\r
103                 for (; p1 < pchCommand + COMMAND_SIZE; p1++)\r
104                     if (*p1 != 0)\r
105                         return false;\r
106             }\r
107             else if (*p1 < ' ' || *p1 > 0x7E)\r
108                 return false;\r
109         }\r
110 \r
111         // Message size\r
112         if (nMessageSize > 0x10000000)\r
113         {\r
114             printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize);\r
115             return false;\r
116         }\r
117 \r
118         return true;\r
119     }\r
120 };\r
121 \r
122 \r
123 \r
124 \r
125 \r
126 \r
127 static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };\r
128 \r
129 class CAddress\r
130 {\r
131 public:\r
132     uint64 nServices;\r
133     unsigned char pchReserved[12];\r
134     unsigned int ip;\r
135     unsigned short port;\r
136 \r
137     // disk only\r
138     unsigned int nTime;\r
139 \r
140     // memory only\r
141     unsigned int nLastTry;\r
142 \r
143     CAddress()\r
144     {\r
145         Init();\r
146     }\r
147 \r
148     CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=NODE_NETWORK)\r
149     {\r
150         Init();\r
151         ip = ipIn;\r
152         port = portIn;\r
153         nServices = nServicesIn;\r
154     }\r
155 \r
156     explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)\r
157     {\r
158         Init();\r
159         ip = sockaddr.sin_addr.s_addr;\r
160         port = sockaddr.sin_port;\r
161         nServices = nServicesIn;\r
162     }\r
163 \r
164     explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)\r
165     {\r
166         Init();\r
167         SetAddress(pszIn);\r
168         nServices = nServicesIn;\r
169     }\r
170 \r
171     explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)\r
172     {\r
173         Init();\r
174         SetAddress(strIn.c_str());\r
175         nServices = nServicesIn;\r
176     }\r
177 \r
178     void Init()\r
179     {\r
180         nServices = NODE_NETWORK;\r
181         memcpy(pchReserved, pchIPv4, sizeof(pchReserved));\r
182         ip = INADDR_NONE;\r
183         port = DEFAULT_PORT;\r
184         nTime = GetAdjustedTime();\r
185         nLastTry = 0;\r
186     }\r
187 \r
188     bool SetAddress(const char* pszIn)\r
189     {\r
190         ip = INADDR_NONE;\r
191         port = DEFAULT_PORT;\r
192         char psz[100];\r
193         strlcpy(psz, pszIn, sizeof(psz));\r
194         unsigned int a=0, b=0, c=0, d=0, e=0;\r
195         if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)\r
196             return false;\r
197         char* pszPort = strchr(psz, ':');\r
198         if (pszPort)\r
199         {\r
200             *pszPort++ = '\0';\r
201             port = htons(atoi(pszPort));\r
202             if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)\r
203                 port = htons(USHRT_MAX);\r
204         }\r
205         ip = inet_addr(psz);\r
206         return IsValid();\r
207     }\r
208 \r
209     bool SetAddress(string strIn)\r
210     {\r
211         return SetAddress(strIn.c_str());\r
212     }\r
213 \r
214     IMPLEMENT_SERIALIZE\r
215     (\r
216         if (nType & SER_DISK)\r
217         {\r
218             READWRITE(nVersion);\r
219             READWRITE(nTime);\r
220         }\r
221         READWRITE(nServices);\r
222         READWRITE(FLATDATA(pchReserved)); // for IPv6\r
223         READWRITE(ip);\r
224         READWRITE(port);\r
225     )\r
226 \r
227     friend inline bool operator==(const CAddress& a, const CAddress& b)\r
228     {\r
229         return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 &&\r
230                 a.ip   == b.ip &&\r
231                 a.port == b.port);\r
232     }\r
233 \r
234     friend inline bool operator!=(const CAddress& a, const CAddress& b)\r
235     {\r
236         return (!(a == b));\r
237     }\r
238 \r
239     friend inline bool operator<(const CAddress& a, const CAddress& b)\r
240     {\r
241         int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved));\r
242         if (ret < 0)\r
243             return true;\r
244         else if (ret == 0)\r
245         {\r
246             if (ntohl(a.ip) < ntohl(b.ip))\r
247                 return true;\r
248             else if (a.ip == b.ip)\r
249                 return ntohs(a.port) < ntohs(b.port);\r
250         }\r
251         return false;\r
252     }\r
253 \r
254     vector<unsigned char> GetKey() const\r
255     {\r
256         CDataStream ss;\r
257         ss.reserve(18);\r
258         ss << FLATDATA(pchReserved) << ip << port;\r
259 \r
260         #if defined(_MSC_VER) && _MSC_VER < 1300\r
261         return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]);\r
262         #else\r
263         return vector<unsigned char>(ss.begin(), ss.end());\r
264         #endif\r
265     }\r
266 \r
267     struct sockaddr_in GetSockAddr() const\r
268     {\r
269         struct sockaddr_in sockaddr;\r
270         memset(&sockaddr, 0, sizeof(sockaddr));\r
271         sockaddr.sin_family = AF_INET;\r
272         sockaddr.sin_addr.s_addr = ip;\r
273         sockaddr.sin_port = port;\r
274         return sockaddr;\r
275     }\r
276 \r
277     bool IsIPv4() const\r
278     {\r
279         return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0);\r
280     }\r
281 \r
282     bool IsRoutable() const\r
283     {\r
284         return !(GetByte(3) == 10 ||\r
285                  (GetByte(3) == 192 && GetByte(2) == 168) ||\r
286                  GetByte(3) == 127 ||\r
287                  GetByte(3) == 0 ||\r
288                  ip == 0 ||\r
289                  ip == INADDR_NONE);\r
290     }\r
291 \r
292     bool IsValid() const\r
293     {\r
294         return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX));\r
295     }\r
296 \r
297     unsigned char GetByte(int n) const\r
298     {\r
299         return ((unsigned char*)&ip)[3-n];\r
300     }\r
301 \r
302     string ToStringIPPort() const\r
303     {\r
304         return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
305     }\r
306 \r
307     string ToStringIP() const\r
308     {\r
309         return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));\r
310     }\r
311 \r
312     string ToStringPort() const\r
313     {\r
314         return strprintf("%u", ntohs(port));\r
315     }\r
316 \r
317     string ToStringLog() const\r
318     {\r
319         return "";\r
320     }\r
321 \r
322     string ToString() const\r
323     {\r
324         return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port));\r
325     }\r
326 \r
327     void print() const\r
328     {\r
329         printf("CAddress(%s)\n", ToString().c_str());\r
330     }\r
331 };\r
332 \r
333 \r
334 \r
335 \r
336 \r
337 \r
338 \r
339 enum\r
340 {\r
341     MSG_TX = 1,\r
342     MSG_BLOCK,\r
343     MSG_REVIEW,\r
344     MSG_PRODUCT,\r
345     MSG_TABLE,\r
346 };\r
347 \r
348 static const char* ppszTypeName[] =\r
349 {\r
350     "ERROR",\r
351     "tx",\r
352     "block",\r
353     "review",\r
354     "product",\r
355     "table",\r
356 };\r
357 \r
358 class CInv\r
359 {\r
360 public:\r
361     int type;\r
362     uint256 hash;\r
363 \r
364     CInv()\r
365     {\r
366         type = 0;\r
367         hash = 0;\r
368     }\r
369 \r
370     CInv(int typeIn, const uint256& hashIn)\r
371     {\r
372         type = typeIn;\r
373         hash = hashIn;\r
374     }\r
375 \r
376     CInv(const string& strType, const uint256& hashIn)\r
377     {\r
378         int i;\r
379         for (i = 1; i < ARRAYLEN(ppszTypeName); i++)\r
380         {\r
381             if (strType == ppszTypeName[i])\r
382             {\r
383                 type = i;\r
384                 break;\r
385             }\r
386         }\r
387         if (i == ARRAYLEN(ppszTypeName))\r
388             throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str()));\r
389         hash = hashIn;\r
390     }\r
391 \r
392     IMPLEMENT_SERIALIZE\r
393     (\r
394         READWRITE(type);\r
395         READWRITE(hash);\r
396     )\r
397 \r
398     friend inline bool operator<(const CInv& a, const CInv& b)\r
399     {\r
400         return (a.type < b.type || (a.type == b.type && a.hash < b.hash));\r
401     }\r
402 \r
403     bool IsKnownType() const\r
404     {\r
405         return (type >= 1 && type < ARRAYLEN(ppszTypeName));\r
406     }\r
407 \r
408     const char* GetCommand() const\r
409     {\r
410         if (!IsKnownType())\r
411             throw std::out_of_range(strprintf("CInv::GetCommand() : type=% unknown type", type));\r
412         return ppszTypeName[type];\r
413     }\r
414 \r
415     string ToString() const\r
416     {\r
417         return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,16).c_str());\r
418     }\r
419 \r
420     void print() const\r
421     {\r
422         printf("CInv(%s)\n", ToString().c_str());\r
423     }\r
424 };\r
425 \r
426 \r
427 \r
428 \r
429 \r
430 class CRequestTracker\r
431 {\r
432 public:\r
433     void (*fn)(void*, CDataStream&);\r
434     void* param1;\r
435 \r
436     explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)\r
437     {\r
438         fn = fnIn;\r
439         param1 = param1In;\r
440     }\r
441 \r
442     bool IsNull()\r
443     {\r
444         return fn == NULL;\r
445     }\r
446 };\r
447 \r
448 \r
449 \r
450 \r
451 \r
452 extern bool fClient;\r
453 extern uint64 nLocalServices;\r
454 extern CAddress addrLocalHost;\r
455 extern CNode* pnodeLocalHost;\r
456 extern uint64 nLocalHostNonce;\r
457 extern array<int, 10> vnThreadsRunning;\r
458 extern SOCKET hListenSocket;\r
459 extern int64 nThreadSocketHandlerHeartbeat;\r
460 \r
461 extern vector<CNode*> vNodes;\r
462 extern CCriticalSection cs_vNodes;\r
463 extern map<vector<unsigned char>, CAddress> mapAddresses;\r
464 extern CCriticalSection cs_mapAddresses;\r
465 extern map<CInv, CDataStream> mapRelay;\r
466 extern deque<pair<int64, CInv> > vRelayExpiration;\r
467 extern CCriticalSection cs_mapRelay;\r
468 extern map<CInv, int64> mapAlreadyAskedFor;\r
469 \r
470 // Settings\r
471 extern int fUseProxy;\r
472 extern CAddress addrProxy;\r
473 \r
474 \r
475 \r
476 \r
477 \r
478 class CNode\r
479 {\r
480 public:\r
481     // socket\r
482     uint64 nServices;\r
483     SOCKET hSocket;\r
484     CDataStream vSend;\r
485     CDataStream vRecv;\r
486     CCriticalSection cs_vSend;\r
487     CCriticalSection cs_vRecv;\r
488     int64 nLastSend;\r
489     int64 nLastRecv;\r
490     int64 nLastSendEmpty;\r
491     int64 nTimeConnected;\r
492     unsigned int nPushPos;\r
493     CAddress addr;\r
494     int nVersion;\r
495     bool fClient;\r
496     bool fInbound;\r
497     bool fNetworkNode;\r
498     bool fSuccessfullyConnected;\r
499     bool fDisconnect;\r
500 protected:\r
501     int nRefCount;\r
502 public:\r
503     int64 nReleaseTime;\r
504     map<uint256, CRequestTracker> mapRequests;\r
505     CCriticalSection cs_mapRequests;\r
506     uint256 hashContinue;\r
507 \r
508     // flood\r
509     vector<CAddress> vAddrToSend;\r
510     set<CAddress> setAddrKnown;\r
511     bool fGetAddr;\r
512 \r
513     // inventory based relay\r
514     set<CInv> setInventoryKnown;\r
515     vector<CInv> vInventoryToSend;\r
516     CCriticalSection cs_inventory;\r
517     multimap<int64, CInv> mapAskFor;\r
518 \r
519     // publish and subscription\r
520     vector<char> vfSubscribe;\r
521 \r
522 \r
523     CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)\r
524     {\r
525         nServices = 0;\r
526         hSocket = hSocketIn;\r
527         vSend.SetType(SER_NETWORK);\r
528         vRecv.SetType(SER_NETWORK);\r
529         nLastSend = 0;\r
530         nLastRecv = 0;\r
531         nLastSendEmpty = GetTime();\r
532         nTimeConnected = GetTime();\r
533         nPushPos = -1;\r
534         addr = addrIn;\r
535         nVersion = 0;\r
536         fClient = false; // set by version message\r
537         fInbound = fInboundIn;\r
538         fNetworkNode = false;\r
539         fSuccessfullyConnected = false;\r
540         fDisconnect = false;\r
541         nRefCount = 0;\r
542         nReleaseTime = 0;\r
543         hashContinue = 0;\r
544         fGetAddr = false;\r
545         vfSubscribe.assign(256, false);\r
546 \r
547         // Push a version message\r
548         /// when NTP implemented, change to just nTime = GetAdjustedTime()\r
549         int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());\r
550         CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);\r
551         CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);\r
552         RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));\r
553         PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, string(pszSubVer));\r
554     }\r
555 \r
556     ~CNode()\r
557     {\r
558         if (hSocket != INVALID_SOCKET)\r
559         {\r
560             closesocket(hSocket);\r
561             hSocket = INVALID_SOCKET;\r
562         }\r
563     }\r
564 \r
565 private:\r
566     CNode(const CNode&);\r
567     void operator=(const CNode&);\r
568 public:\r
569 \r
570 \r
571     int GetRefCount()\r
572     {\r
573         return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);\r
574     }\r
575 \r
576     CNode* AddRef(int64 nTimeout=0)\r
577     {\r
578         if (nTimeout != 0)\r
579             nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);\r
580         else\r
581             nRefCount++;\r
582         return this;\r
583     }\r
584 \r
585     void Release()\r
586     {\r
587         nRefCount--;\r
588     }\r
589 \r
590 \r
591 \r
592     void AddAddressKnown(const CAddress& addr)\r
593     {\r
594         setAddrKnown.insert(addr);\r
595     }\r
596 \r
597     void PushAddress(const CAddress& addr)\r
598     {\r
599         // Known checking here is only to save space from duplicates.\r
600         // SendMessages will filter it again for knowns that were added\r
601         // after addresses were pushed.\r
602         if (!setAddrKnown.count(addr))\r
603             vAddrToSend.push_back(addr);\r
604     }\r
605 \r
606 \r
607     void AddInventoryKnown(const CInv& inv)\r
608     {\r
609         CRITICAL_BLOCK(cs_inventory)\r
610             setInventoryKnown.insert(inv);\r
611     }\r
612 \r
613     void PushInventory(const CInv& inv)\r
614     {\r
615         CRITICAL_BLOCK(cs_inventory)\r
616             if (!setInventoryKnown.count(inv))\r
617                 vInventoryToSend.push_back(inv);\r
618     }\r
619 \r
620     void AskFor(const CInv& inv)\r
621     {\r
622         // We're using mapAskFor as a priority queue,\r
623         // the key is the earliest time the request can be sent\r
624         int64& nRequestTime = mapAlreadyAskedFor[inv];\r
625         printf("askfor %s  %"PRI64d"\n", inv.ToString().c_str(), nRequestTime);\r
626 \r
627         // Make sure not to reuse time indexes to keep things in the same order\r
628         int64 nNow = (GetTime() - 1) * 1000000;\r
629         static int64 nLastTime;\r
630         nLastTime = nNow = max(nNow, ++nLastTime);\r
631 \r
632         // Each retry is 2 minutes after the last\r
633         nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow);\r
634         mapAskFor.insert(make_pair(nRequestTime, inv));\r
635     }\r
636 \r
637 \r
638     void BeginMessage(const char* pszCommand)\r
639     {\r
640         cs_vSend.Enter();\r
641         if (nPushPos != -1)\r
642             AbortMessage();\r
643         nPushPos = vSend.size();\r
644         vSend << CMessageHeader(pszCommand, 0);\r
645         if (fDebug)\r
646             printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
647         printf("sending: %s ", pszCommand);\r
648     }\r
649 \r
650     void AbortMessage()\r
651     {\r
652         if (nPushPos == -1)\r
653             return;\r
654         vSend.resize(nPushPos);\r
655         nPushPos = -1;\r
656         cs_vSend.Leave();\r
657         printf("(aborted)\n");\r
658     }\r
659 \r
660     void EndMessage()\r
661     {\r
662         if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)\r
663         {\r
664             printf("dropmessages DROPPING SEND MESSAGE\n");\r
665             AbortMessage();\r
666             return;\r
667         }\r
668 \r
669         if (nPushPos == -1)\r
670             return;\r
671 \r
672         // Patch in the size\r
673         unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
674         memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));\r
675 \r
676         printf("(%d bytes) ", nSize);\r
677         printf("\n");\r
678 \r
679         nPushPos = -1;\r
680         cs_vSend.Leave();\r
681     }\r
682 \r
683     void EndMessageAbortIfEmpty()\r
684     {\r
685         if (nPushPos == -1)\r
686             return;\r
687         int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
688         if (nSize > 0)\r
689             EndMessage();\r
690         else\r
691             AbortMessage();\r
692     }\r
693 \r
694     const char* GetMessageCommand() const\r
695     {\r
696         if (nPushPos == -1)\r
697             return "";\r
698         return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand);\r
699     }\r
700 \r
701 \r
702 \r
703 \r
704     void PushMessage(const char* pszCommand)\r
705     {\r
706         try\r
707         {\r
708             BeginMessage(pszCommand);\r
709             EndMessage();\r
710         }\r
711         catch (...)\r
712         {\r
713             AbortMessage();\r
714             throw;\r
715         }\r
716     }\r
717 \r
718     template<typename T1>\r
719     void PushMessage(const char* pszCommand, const T1& a1)\r
720     {\r
721         try\r
722         {\r
723             BeginMessage(pszCommand);\r
724             vSend << a1;\r
725             EndMessage();\r
726         }\r
727         catch (...)\r
728         {\r
729             AbortMessage();\r
730             throw;\r
731         }\r
732     }\r
733 \r
734     template<typename T1, typename T2>\r
735     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)\r
736     {\r
737         try\r
738         {\r
739             BeginMessage(pszCommand);\r
740             vSend << a1 << a2;\r
741             EndMessage();\r
742         }\r
743         catch (...)\r
744         {\r
745             AbortMessage();\r
746             throw;\r
747         }\r
748     }\r
749 \r
750     template<typename T1, typename T2, typename T3>\r
751     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)\r
752     {\r
753         try\r
754         {\r
755             BeginMessage(pszCommand);\r
756             vSend << a1 << a2 << a3;\r
757             EndMessage();\r
758         }\r
759         catch (...)\r
760         {\r
761             AbortMessage();\r
762             throw;\r
763         }\r
764     }\r
765 \r
766     template<typename T1, typename T2, typename T3, typename T4>\r
767     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)\r
768     {\r
769         try\r
770         {\r
771             BeginMessage(pszCommand);\r
772             vSend << a1 << a2 << a3 << a4;\r
773             EndMessage();\r
774         }\r
775         catch (...)\r
776         {\r
777             AbortMessage();\r
778             throw;\r
779         }\r
780     }\r
781 \r
782     template<typename T1, typename T2, typename T3, typename T4, typename T5>\r
783     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)\r
784     {\r
785         try\r
786         {\r
787             BeginMessage(pszCommand);\r
788             vSend << a1 << a2 << a3 << a4 << a5;\r
789             EndMessage();\r
790         }\r
791         catch (...)\r
792         {\r
793             AbortMessage();\r
794             throw;\r
795         }\r
796     }\r
797 \r
798     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>\r
799     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)\r
800     {\r
801         try\r
802         {\r
803             BeginMessage(pszCommand);\r
804             vSend << a1 << a2 << a3 << a4 << a5 << a6;\r
805             EndMessage();\r
806         }\r
807         catch (...)\r
808         {\r
809             AbortMessage();\r
810             throw;\r
811         }\r
812     }\r
813 \r
814     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>\r
815     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
816     {\r
817         try\r
818         {\r
819             BeginMessage(pszCommand);\r
820             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;\r
821             EndMessage();\r
822         }\r
823         catch (...)\r
824         {\r
825             AbortMessage();\r
826             throw;\r
827         }\r
828     }\r
829 \r
830     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>\r
831     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
832     {\r
833         try\r
834         {\r
835             BeginMessage(pszCommand);\r
836             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;\r
837             EndMessage();\r
838         }\r
839         catch (...)\r
840         {\r
841             AbortMessage();\r
842             throw;\r
843         }\r
844     }\r
845 \r
846     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>\r
847     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
848     {\r
849         try\r
850         {\r
851             BeginMessage(pszCommand);\r
852             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;\r
853             EndMessage();\r
854         }\r
855         catch (...)\r
856         {\r
857             AbortMessage();\r
858             throw;\r
859         }\r
860     }\r
861 \r
862 \r
863     void PushRequest(const char* pszCommand,\r
864                      void (*fn)(void*, CDataStream&), void* param1)\r
865     {\r
866         uint256 hashReply;\r
867         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
868 \r
869         CRITICAL_BLOCK(cs_mapRequests)\r
870             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
871 \r
872         PushMessage(pszCommand, hashReply);\r
873     }\r
874 \r
875     template<typename T1>\r
876     void PushRequest(const char* pszCommand, const T1& a1,\r
877                      void (*fn)(void*, CDataStream&), void* param1)\r
878     {\r
879         uint256 hashReply;\r
880         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
881 \r
882         CRITICAL_BLOCK(cs_mapRequests)\r
883             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
884 \r
885         PushMessage(pszCommand, hashReply, a1);\r
886     }\r
887 \r
888     template<typename T1, typename T2>\r
889     void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,\r
890                      void (*fn)(void*, CDataStream&), void* param1)\r
891     {\r
892         uint256 hashReply;\r
893         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
894 \r
895         CRITICAL_BLOCK(cs_mapRequests)\r
896             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
897 \r
898         PushMessage(pszCommand, hashReply, a1, a2);\r
899     }\r
900 \r
901 \r
902 \r
903     bool IsSubscribed(unsigned int nChannel);\r
904     void Subscribe(unsigned int nChannel, unsigned int nHops=0);\r
905     void CancelSubscribe(unsigned int nChannel);\r
906     void CloseSocketDisconnect();\r
907     void Cleanup();\r
908 };\r
909 \r
910 \r
911 \r
912 \r
913 \r
914 \r
915 \r
916 \r
917 \r
918 \r
919 inline void RelayInventory(const CInv& inv)\r
920 {\r
921     // Put on lists to offer to the other nodes\r
922     CRITICAL_BLOCK(cs_vNodes)\r
923         foreach(CNode* pnode, vNodes)\r
924             pnode->PushInventory(inv);\r
925 }\r
926 \r
927 template<typename T>\r
928 void RelayMessage(const CInv& inv, const T& a)\r
929 {\r
930     CDataStream ss(SER_NETWORK);\r
931     ss.reserve(10000);\r
932     ss << a;\r
933     RelayMessage(inv, ss);\r
934 }\r
935 \r
936 template<>\r
937 inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)\r
938 {\r
939     CRITICAL_BLOCK(cs_mapRelay)\r
940     {\r
941         // Expire old relay messages\r
942         while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())\r
943         {\r
944             mapRelay.erase(vRelayExpiration.front().second);\r
945             vRelayExpiration.pop_front();\r
946         }\r
947 \r
948         // Save original serialized message so newer versions are preserved\r
949         mapRelay[inv] = ss;\r
950         vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));\r
951     }\r
952 \r
953     RelayInventory(inv);\r
954 }\r
955 \r
956 \r
957 \r
958 \r
959 \r
960 \r
961 \r
962 \r
963 //\r
964 // Templates for the publish and subscription system.\r
965 // The object being published as T& obj needs to have:\r
966 //   a set<unsigned int> setSources member\r
967 //   specializations of AdvertInsert and AdvertErase\r
968 // Currently implemented for CTable and CProduct.\r
969 //\r
970 \r
971 template<typename T>\r
972 void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
973 {\r
974     // Add to sources\r
975     obj.setSources.insert(pfrom->addr.ip);\r
976 \r
977     if (!AdvertInsert(obj))\r
978         return;\r
979 \r
980     // Relay\r
981     CRITICAL_BLOCK(cs_vNodes)\r
982         foreach(CNode* pnode, vNodes)\r
983             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
984                 pnode->PushMessage("publish", nChannel, nHops, obj);\r
985 }\r
986 \r
987 template<typename T>\r
988 void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
989 {\r
990     uint256 hash = obj.GetHash();\r
991 \r
992     CRITICAL_BLOCK(cs_vNodes)\r
993         foreach(CNode* pnode, vNodes)\r
994             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
995                 pnode->PushMessage("pub-cancel", nChannel, nHops, hash);\r
996 \r
997     AdvertErase(obj);\r
998 }\r
999 \r
1000 template<typename T>\r
1001 void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
1002 {\r
1003     // Remove a source\r
1004     obj.setSources.erase(pfrom->addr.ip);\r
1005 \r
1006     // If no longer supported by any sources, cancel it\r
1007     if (obj.setSources.empty())\r
1008         AdvertStopPublish(pfrom, nChannel, nHops, obj);\r
1009 }\r