unix build merged in, bitmap resources from xpm instead of rc, better addr relay...
[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 StartNode(string& strError=REF(string()));\r
32 bool StopNode();\r
33 void CheckForShutdown(int n);\r
34 \r
35 \r
36 \r
37 \r
38 \r
39 \r
40 \r
41 \r
42 \r
43 //\r
44 // Message header\r
45 //  (4) message start\r
46 //  (12) command\r
47 //  (4) size\r
48 \r
49 // The message start string is designed to be unlikely to occur in normal data.\r
50 // The characters are rarely used upper ascii, not valid as UTF-8, and produce\r
51 // a large 4-byte int at any alignment.\r
52 static const char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };\r
53 \r
54 class CMessageHeader\r
55 {\r
56 public:\r
57     enum { COMMAND_SIZE=12 };\r
58     char pchMessageStart[sizeof(::pchMessageStart)];\r
59     char pchCommand[COMMAND_SIZE];\r
60     unsigned int nMessageSize;\r
61 \r
62     CMessageHeader()\r
63     {\r
64         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
65         memset(pchCommand, 0, sizeof(pchCommand));\r
66         pchCommand[1] = 1;\r
67         nMessageSize = -1;\r
68     }\r
69 \r
70     CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)\r
71     {\r
72         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));\r
73         strncpy(pchCommand, pszCommand, COMMAND_SIZE);\r
74         nMessageSize = nMessageSizeIn;\r
75     }\r
76 \r
77     IMPLEMENT_SERIALIZE\r
78     (\r
79         READWRITE(FLATDATA(pchMessageStart));\r
80         READWRITE(FLATDATA(pchCommand));\r
81         READWRITE(nMessageSize);\r
82     )\r
83 \r
84     string GetCommand()\r
85     {\r
86         if (pchCommand[COMMAND_SIZE-1] == 0)\r
87             return string(pchCommand, pchCommand + strlen(pchCommand));\r
88         else\r
89             return string(pchCommand, pchCommand + COMMAND_SIZE);\r
90     }\r
91 \r
92     bool IsValid()\r
93     {\r
94         // Check start string\r
95         if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0)\r
96             return false;\r
97 \r
98         // Check the command string for errors\r
99         for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)\r
100         {\r
101             if (*p1 == 0)\r
102             {\r
103                 // Must be all zeros after the first zero\r
104                 for (; p1 < pchCommand + COMMAND_SIZE; p1++)\r
105                     if (*p1 != 0)\r
106                         return false;\r
107             }\r
108             else if (*p1 < ' ' || *p1 > 0x7E)\r
109                 return false;\r
110         }\r
111 \r
112         // Message size\r
113         if (nMessageSize > 0x10000000)\r
114         {\r
115             printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize);\r
116             return false;\r
117         }\r
118 \r
119         return true;\r
120     }\r
121 };\r
122 \r
123 \r
124 \r
125 \r
126 \r
127 \r
128 static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };\r
129 \r
130 class CAddress\r
131 {\r
132 public:\r
133     uint64 nServices;\r
134     unsigned char pchReserved[12];\r
135     unsigned int ip;\r
136     unsigned short port;\r
137 \r
138     // disk only\r
139     unsigned int nTime;\r
140 \r
141     // memory only\r
142     unsigned int nLastFailed;\r
143 \r
144     CAddress()\r
145     {\r
146         Init();\r
147     }\r
148 \r
149     CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=NODE_NETWORK)\r
150     {\r
151         Init();\r
152         ip = ipIn;\r
153         port = portIn;\r
154         nServices = nServicesIn;\r
155     }\r
156 \r
157     explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)\r
158     {\r
159         Init();\r
160         ip = sockaddr.sin_addr.s_addr;\r
161         port = sockaddr.sin_port;\r
162         nServices = nServicesIn;\r
163     }\r
164 \r
165     explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)\r
166     {\r
167         Init();\r
168         SetAddress(pszIn);\r
169         nServices = nServicesIn;\r
170     }\r
171 \r
172     explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)\r
173     {\r
174         Init();\r
175         SetAddress(strIn.c_str());\r
176         nServices = nServicesIn;\r
177     }\r
178 \r
179     void Init()\r
180     {\r
181         nServices = NODE_NETWORK;\r
182         memcpy(pchReserved, pchIPv4, sizeof(pchReserved));\r
183         ip = INADDR_NONE;\r
184         port = DEFAULT_PORT;\r
185         nTime = GetAdjustedTime();\r
186         nLastFailed = 0;\r
187     }\r
188 \r
189     bool SetAddress(const char* pszIn)\r
190     {\r
191         ip = INADDR_NONE;\r
192         port = DEFAULT_PORT;\r
193         char psz[100];\r
194         strlcpy(psz, pszIn, sizeof(psz));\r
195         unsigned int a=0, b=0, c=0, d=0, e=0;\r
196         if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)\r
197             return false;\r
198         char* pszPort = strchr(psz, ':');\r
199         if (pszPort)\r
200         {\r
201             *pszPort++ = '\0';\r
202             port = htons(atoi(pszPort));\r
203             if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)\r
204                 port = htons(USHRT_MAX);\r
205         }\r
206         ip = inet_addr(psz);\r
207         return IsValid();\r
208     }\r
209 \r
210     bool SetAddress(string strIn)\r
211     {\r
212         return SetAddress(strIn.c_str());\r
213     }\r
214 \r
215     IMPLEMENT_SERIALIZE\r
216     (\r
217         if (nType & SER_DISK)\r
218         {\r
219             READWRITE(nVersion);\r
220             READWRITE(nTime);\r
221         }\r
222         READWRITE(nServices);\r
223         READWRITE(FLATDATA(pchReserved)); // for IPv6\r
224         READWRITE(ip);\r
225         READWRITE(port);\r
226     )\r
227 \r
228     friend inline bool operator==(const CAddress& a, const CAddress& b)\r
229     {\r
230         return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 &&\r
231                 a.ip   == b.ip &&\r
232                 a.port == b.port);\r
233     }\r
234 \r
235     friend inline bool operator!=(const CAddress& a, const CAddress& b)\r
236     {\r
237         return (!(a == b));\r
238     }\r
239 \r
240     friend inline bool operator<(const CAddress& a, const CAddress& b)\r
241     {\r
242         int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved));\r
243         if (ret < 0)\r
244             return true;\r
245         else if (ret == 0)\r
246         {\r
247             if (ntohl(a.ip) < ntohl(b.ip))\r
248                 return true;\r
249             else if (a.ip == b.ip)\r
250                 return ntohs(a.port) < ntohs(b.port);\r
251         }\r
252         return false;\r
253     }\r
254 \r
255     vector<unsigned char> GetKey() const\r
256     {\r
257         CDataStream ss;\r
258         ss.reserve(18);\r
259         ss << FLATDATA(pchReserved) << ip << port;\r
260 \r
261         #if defined(_MSC_VER) && _MSC_VER < 1300\r
262         return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]);\r
263         #else\r
264         return vector<unsigned char>(ss.begin(), ss.end());\r
265         #endif\r
266     }\r
267 \r
268     struct sockaddr_in GetSockAddr() const\r
269     {\r
270         struct sockaddr_in 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,14).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 bool fShutdown;\r
458 extern array<int, 10> vnThreadsRunning;\r
459 extern vector<CNode*> vNodes;\r
460 extern CCriticalSection cs_vNodes;\r
461 extern map<vector<unsigned char>, CAddress> mapAddresses;\r
462 extern CCriticalSection cs_mapAddresses;\r
463 extern map<CInv, CDataStream> mapRelay;\r
464 extern deque<pair<int64, CInv> > vRelayExpiration;\r
465 extern CCriticalSection cs_mapRelay;\r
466 extern map<CInv, int64> mapAlreadyAskedFor;\r
467 \r
468 // Settings\r
469 extern int fUseProxy;\r
470 extern CAddress addrProxy;\r
471 \r
472 \r
473 \r
474 \r
475 \r
476 class CNode\r
477 {\r
478 public:\r
479     // socket\r
480     uint64 nServices;\r
481     SOCKET hSocket;\r
482     CDataStream vSend;\r
483     CDataStream vRecv;\r
484     CCriticalSection cs_vSend;\r
485     CCriticalSection cs_vRecv;\r
486     unsigned int nPushPos;\r
487     CAddress addr;\r
488     int nVersion;\r
489     bool fClient;\r
490     bool fInbound;\r
491     bool fNetworkNode;\r
492     bool fSuccessfullyConnected;\r
493     bool fDisconnect;\r
494 protected:\r
495     int nRefCount;\r
496 public:\r
497     int64 nReleaseTime;\r
498     map<uint256, CRequestTracker> mapRequests;\r
499     CCriticalSection cs_mapRequests;\r
500 \r
501     // flood\r
502     vector<CAddress> vAddrToSend;\r
503     set<CAddress> setAddrKnown;\r
504     bool fGetAddr;\r
505 \r
506     // inventory based relay\r
507     set<CInv> setInventoryKnown;\r
508     set<CInv> setInventoryKnown2;\r
509     vector<CInv> vInventoryToSend;\r
510     CCriticalSection cs_inventory;\r
511     multimap<int64, CInv> mapAskFor;\r
512 \r
513     // publish and subscription\r
514     vector<char> vfSubscribe;\r
515 \r
516 \r
517     CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)\r
518     {\r
519         nServices = 0;\r
520         hSocket = hSocketIn;\r
521         vSend.SetType(SER_NETWORK);\r
522         vRecv.SetType(SER_NETWORK);\r
523         nPushPos = -1;\r
524         addr = addrIn;\r
525         nVersion = 0;\r
526         fClient = false; // set by version message\r
527         fInbound = fInboundIn;\r
528         fNetworkNode = false;\r
529         fSuccessfullyConnected = false;\r
530         fDisconnect = false;\r
531         nRefCount = 0;\r
532         nReleaseTime = 0;\r
533         fGetAddr = false;\r
534         vfSubscribe.assign(256, false);\r
535 \r
536         // Push a version message\r
537         /// when NTP implemented, change to just nTime = GetAdjustedTime()\r
538         int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());\r
539         CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);\r
540         CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);\r
541         RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));\r
542         PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce);\r
543     }\r
544 \r
545     ~CNode()\r
546     {\r
547         if (hSocket != INVALID_SOCKET)\r
548             closesocket(hSocket);\r
549     }\r
550 \r
551 private:\r
552     CNode(const CNode&);\r
553     void operator=(const CNode&);\r
554 public:\r
555 \r
556 \r
557     bool ReadyToDisconnect()\r
558     {\r
559         return fDisconnect || GetRefCount() <= 0;\r
560     }\r
561 \r
562     int GetRefCount()\r
563     {\r
564         return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);\r
565     }\r
566 \r
567     void AddRef(int64 nTimeout=0)\r
568     {\r
569         if (nTimeout != 0)\r
570             nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);\r
571         else\r
572             nRefCount++;\r
573     }\r
574 \r
575     void Release()\r
576     {\r
577         nRefCount--;\r
578     }\r
579 \r
580 \r
581 \r
582     void AddAddressKnown(const CAddress& addr)\r
583     {\r
584         setAddrKnown.insert(addr);\r
585     }\r
586 \r
587     void PushAddress(const CAddress& addr)\r
588     {\r
589         // Known checking here is only to save space from duplicates.\r
590         // SendMessages will filter it again for knowns that were added\r
591         // after addresses were pushed.\r
592         if (!setAddrKnown.count(addr))\r
593             vAddrToSend.push_back(addr);\r
594     }\r
595 \r
596 \r
597     void AddInventoryKnown(const CInv& inv)\r
598     {\r
599         CRITICAL_BLOCK(cs_inventory)\r
600             setInventoryKnown.insert(inv);\r
601     }\r
602 \r
603     void PushInventory(const CInv& inv)\r
604     {\r
605         CRITICAL_BLOCK(cs_inventory)\r
606             if (!setInventoryKnown.count(inv))\r
607                 vInventoryToSend.push_back(inv);\r
608     }\r
609 \r
610     void AskFor(const CInv& inv)\r
611     {\r
612         // We're using mapAskFor as a priority queue,\r
613         // the key is the earliest time the request can be sent\r
614         int64& nRequestTime = mapAlreadyAskedFor[inv];\r
615         printf("askfor %s  %"PRI64d"\n", inv.ToString().c_str(), nRequestTime);\r
616 \r
617         // Make sure not to reuse time indexes to keep things in the same order\r
618         int64 nNow = (GetTime() - 1) * 1000000;\r
619         static int64 nLastTime;\r
620         nLastTime = nNow = max(nNow, ++nLastTime);\r
621 \r
622         // Each retry is 2 minutes after the last\r
623         nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow);\r
624         mapAskFor.insert(make_pair(nRequestTime, inv));\r
625     }\r
626 \r
627 \r
628     void BeginMessage(const char* pszCommand)\r
629     {\r
630         cs_vSend.Enter();\r
631         if (nPushPos != -1)\r
632             AbortMessage();\r
633         nPushPos = vSend.size();\r
634         vSend << CMessageHeader(pszCommand, 0);\r
635         printf("sending: %s ", pszCommand);\r
636     }\r
637 \r
638     void AbortMessage()\r
639     {\r
640         if (nPushPos == -1)\r
641             return;\r
642         vSend.resize(nPushPos);\r
643         nPushPos = -1;\r
644         cs_vSend.Leave();\r
645         printf("(aborted)\n");\r
646     }\r
647 \r
648     void EndMessage()\r
649     {\r
650         extern int nDropMessagesTest;\r
651         if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0)\r
652         {\r
653             printf("dropmessages DROPPING SEND MESSAGE\n");\r
654             AbortMessage();\r
655             return;\r
656         }\r
657 \r
658         if (nPushPos == -1)\r
659             return;\r
660 \r
661         // Patch in the size\r
662         unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
663         memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));\r
664 \r
665         printf("(%d bytes) ", nSize);\r
666         printf("\n");\r
667 \r
668         nPushPos = -1;\r
669         cs_vSend.Leave();\r
670     }\r
671 \r
672     void EndMessageAbortIfEmpty()\r
673     {\r
674         if (nPushPos == -1)\r
675             return;\r
676         int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
677         if (nSize > 0)\r
678             EndMessage();\r
679         else\r
680             AbortMessage();\r
681     }\r
682 \r
683     const char* GetMessageCommand() const\r
684     {\r
685         if (nPushPos == -1)\r
686             return "";\r
687         return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand);\r
688     }\r
689 \r
690 \r
691 \r
692 \r
693     void PushMessage(const char* pszCommand)\r
694     {\r
695         try\r
696         {\r
697             BeginMessage(pszCommand);\r
698             EndMessage();\r
699         }\r
700         catch (...)\r
701         {\r
702             AbortMessage();\r
703             throw;\r
704         }\r
705     }\r
706 \r
707     template<typename T1>\r
708     void PushMessage(const char* pszCommand, const T1& a1)\r
709     {\r
710         try\r
711         {\r
712             BeginMessage(pszCommand);\r
713             vSend << a1;\r
714             EndMessage();\r
715         }\r
716         catch (...)\r
717         {\r
718             AbortMessage();\r
719             throw;\r
720         }\r
721     }\r
722 \r
723     template<typename T1, typename T2>\r
724     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)\r
725     {\r
726         try\r
727         {\r
728             BeginMessage(pszCommand);\r
729             vSend << a1 << a2;\r
730             EndMessage();\r
731         }\r
732         catch (...)\r
733         {\r
734             AbortMessage();\r
735             throw;\r
736         }\r
737     }\r
738 \r
739     template<typename T1, typename T2, typename T3>\r
740     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)\r
741     {\r
742         try\r
743         {\r
744             BeginMessage(pszCommand);\r
745             vSend << a1 << a2 << a3;\r
746             EndMessage();\r
747         }\r
748         catch (...)\r
749         {\r
750             AbortMessage();\r
751             throw;\r
752         }\r
753     }\r
754 \r
755     template<typename T1, typename T2, typename T3, typename T4>\r
756     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)\r
757     {\r
758         try\r
759         {\r
760             BeginMessage(pszCommand);\r
761             vSend << a1 << a2 << a3 << a4;\r
762             EndMessage();\r
763         }\r
764         catch (...)\r
765         {\r
766             AbortMessage();\r
767             throw;\r
768         }\r
769     }\r
770 \r
771     template<typename T1, typename T2, typename T3, typename T4, typename T5>\r
772     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)\r
773     {\r
774         try\r
775         {\r
776             BeginMessage(pszCommand);\r
777             vSend << a1 << a2 << a3 << a4 << a5;\r
778             EndMessage();\r
779         }\r
780         catch (...)\r
781         {\r
782             AbortMessage();\r
783             throw;\r
784         }\r
785     }\r
786 \r
787     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>\r
788     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)\r
789     {\r
790         try\r
791         {\r
792             BeginMessage(pszCommand);\r
793             vSend << a1 << a2 << a3 << a4 << a5 << a6;\r
794             EndMessage();\r
795         }\r
796         catch (...)\r
797         {\r
798             AbortMessage();\r
799             throw;\r
800         }\r
801     }\r
802 \r
803     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>\r
804     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
805     {\r
806         try\r
807         {\r
808             BeginMessage(pszCommand);\r
809             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;\r
810             EndMessage();\r
811         }\r
812         catch (...)\r
813         {\r
814             AbortMessage();\r
815             throw;\r
816         }\r
817     }\r
818 \r
819     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>\r
820     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
821     {\r
822         try\r
823         {\r
824             BeginMessage(pszCommand);\r
825             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;\r
826             EndMessage();\r
827         }\r
828         catch (...)\r
829         {\r
830             AbortMessage();\r
831             throw;\r
832         }\r
833     }\r
834 \r
835     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>\r
836     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
837     {\r
838         try\r
839         {\r
840             BeginMessage(pszCommand);\r
841             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;\r
842             EndMessage();\r
843         }\r
844         catch (...)\r
845         {\r
846             AbortMessage();\r
847             throw;\r
848         }\r
849     }\r
850 \r
851 \r
852     void PushRequest(const char* pszCommand,\r
853                      void (*fn)(void*, CDataStream&), void* param1)\r
854     {\r
855         uint256 hashReply;\r
856         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
857 \r
858         CRITICAL_BLOCK(cs_mapRequests)\r
859             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
860 \r
861         PushMessage(pszCommand, hashReply);\r
862     }\r
863 \r
864     template<typename T1>\r
865     void PushRequest(const char* pszCommand, const T1& a1,\r
866                      void (*fn)(void*, CDataStream&), void* param1)\r
867     {\r
868         uint256 hashReply;\r
869         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
870 \r
871         CRITICAL_BLOCK(cs_mapRequests)\r
872             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
873 \r
874         PushMessage(pszCommand, hashReply, a1);\r
875     }\r
876 \r
877     template<typename T1, typename T2>\r
878     void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,\r
879                      void (*fn)(void*, CDataStream&), void* param1)\r
880     {\r
881         uint256 hashReply;\r
882         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
883 \r
884         CRITICAL_BLOCK(cs_mapRequests)\r
885             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
886 \r
887         PushMessage(pszCommand, hashReply, a1, a2);\r
888     }\r
889 \r
890 \r
891 \r
892     bool IsSubscribed(unsigned int nChannel);\r
893     void Subscribe(unsigned int nChannel, unsigned int nHops=0);\r
894     void CancelSubscribe(unsigned int nChannel);\r
895     void DoDisconnect();\r
896 };\r
897 \r
898 \r
899 \r
900 \r
901 \r
902 \r
903 \r
904 \r
905 \r
906 \r
907 inline void RelayInventory(const CInv& inv)\r
908 {\r
909     // Put on lists to offer to the other nodes\r
910     CRITICAL_BLOCK(cs_vNodes)\r
911         foreach(CNode* pnode, vNodes)\r
912             pnode->PushInventory(inv);\r
913 }\r
914 \r
915 template<typename T>\r
916 void RelayMessage(const CInv& inv, const T& a)\r
917 {\r
918     CDataStream ss(SER_NETWORK);\r
919     ss.reserve(10000);\r
920     ss << a;\r
921     RelayMessage(inv, ss);\r
922 }\r
923 \r
924 template<>\r
925 inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)\r
926 {\r
927     CRITICAL_BLOCK(cs_mapRelay)\r
928     {\r
929         // Expire old relay messages\r
930         while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())\r
931         {\r
932             mapRelay.erase(vRelayExpiration.front().second);\r
933             vRelayExpiration.pop_front();\r
934         }\r
935 \r
936         // Save original serialized message so newer versions are preserved\r
937         mapRelay[inv] = ss;\r
938         vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));\r
939     }\r
940 \r
941     RelayInventory(inv);\r
942 }\r
943 \r
944 \r
945 \r
946 \r
947 \r
948 \r
949 \r
950 \r
951 //\r
952 // Templates for the publish and subscription system.\r
953 // The object being published as T& obj needs to have:\r
954 //   a set<unsigned int> setSources member\r
955 //   specializations of AdvertInsert and AdvertErase\r
956 // Currently implemented for CTable and CProduct.\r
957 //\r
958 \r
959 template<typename T>\r
960 void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
961 {\r
962     // Add to sources\r
963     obj.setSources.insert(pfrom->addr.ip);\r
964 \r
965     if (!AdvertInsert(obj))\r
966         return;\r
967 \r
968     // Relay\r
969     CRITICAL_BLOCK(cs_vNodes)\r
970         foreach(CNode* pnode, vNodes)\r
971             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
972                 pnode->PushMessage("publish", nChannel, nHops, obj);\r
973 }\r
974 \r
975 template<typename T>\r
976 void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
977 {\r
978     uint256 hash = obj.GetHash();\r
979 \r
980     CRITICAL_BLOCK(cs_vNodes)\r
981         foreach(CNode* pnode, vNodes)\r
982             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
983                 pnode->PushMessage("pub-cancel", nChannel, nHops, hash);\r
984 \r
985     AdvertErase(obj);\r
986 }\r
987 \r
988 template<typename T>\r
989 void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
990 {\r
991     // Remove a source\r
992     obj.setSources.erase(pfrom->addr.ip);\r
993 \r
994     // If no longer supported by any sources, cancel it\r
995     if (obj.setSources.empty())\r
996         AdvertStopPublish(pfrom, nChannel, nHops, obj);\r
997 }\r