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