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