strip out unfinished product, review and market stuff,
[novacoin.git] / net.h
1 // Copyright (c) 2009-2010 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 };\r
345 \r
346 static const char* ppszTypeName[] =\r
347 {\r
348     "ERROR",\r
349     "tx",\r
350     "block",\r
351 };\r
352 \r
353 class CInv\r
354 {\r
355 public:\r
356     int type;\r
357     uint256 hash;\r
358 \r
359     CInv()\r
360     {\r
361         type = 0;\r
362         hash = 0;\r
363     }\r
364 \r
365     CInv(int typeIn, const uint256& hashIn)\r
366     {\r
367         type = typeIn;\r
368         hash = hashIn;\r
369     }\r
370 \r
371     CInv(const string& strType, const uint256& hashIn)\r
372     {\r
373         int i;\r
374         for (i = 1; i < ARRAYLEN(ppszTypeName); i++)\r
375         {\r
376             if (strType == ppszTypeName[i])\r
377             {\r
378                 type = i;\r
379                 break;\r
380             }\r
381         }\r
382         if (i == ARRAYLEN(ppszTypeName))\r
383             throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str()));\r
384         hash = hashIn;\r
385     }\r
386 \r
387     IMPLEMENT_SERIALIZE\r
388     (\r
389         READWRITE(type);\r
390         READWRITE(hash);\r
391     )\r
392 \r
393     friend inline bool operator<(const CInv& a, const CInv& b)\r
394     {\r
395         return (a.type < b.type || (a.type == b.type && a.hash < b.hash));\r
396     }\r
397 \r
398     bool IsKnownType() const\r
399     {\r
400         return (type >= 1 && type < ARRAYLEN(ppszTypeName));\r
401     }\r
402 \r
403     const char* GetCommand() const\r
404     {\r
405         if (!IsKnownType())\r
406             throw std::out_of_range(strprintf("CInv::GetCommand() : type=% unknown type", type));\r
407         return ppszTypeName[type];\r
408     }\r
409 \r
410     string ToString() const\r
411     {\r
412         return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,16).c_str());\r
413     }\r
414 \r
415     void print() const\r
416     {\r
417         printf("CInv(%s)\n", ToString().c_str());\r
418     }\r
419 };\r
420 \r
421 \r
422 \r
423 \r
424 \r
425 class CRequestTracker\r
426 {\r
427 public:\r
428     void (*fn)(void*, CDataStream&);\r
429     void* param1;\r
430 \r
431     explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)\r
432     {\r
433         fn = fnIn;\r
434         param1 = param1In;\r
435     }\r
436 \r
437     bool IsNull()\r
438     {\r
439         return fn == NULL;\r
440     }\r
441 };\r
442 \r
443 \r
444 \r
445 \r
446 \r
447 extern bool fClient;\r
448 extern uint64 nLocalServices;\r
449 extern CAddress addrLocalHost;\r
450 extern CNode* pnodeLocalHost;\r
451 extern uint64 nLocalHostNonce;\r
452 extern array<int, 10> vnThreadsRunning;\r
453 extern SOCKET hListenSocket;\r
454 extern int64 nThreadSocketHandlerHeartbeat;\r
455 \r
456 extern vector<CNode*> vNodes;\r
457 extern CCriticalSection cs_vNodes;\r
458 extern map<vector<unsigned char>, CAddress> mapAddresses;\r
459 extern CCriticalSection cs_mapAddresses;\r
460 extern map<CInv, CDataStream> mapRelay;\r
461 extern deque<pair<int64, CInv> > vRelayExpiration;\r
462 extern CCriticalSection cs_mapRelay;\r
463 extern map<CInv, int64> mapAlreadyAskedFor;\r
464 \r
465 // Settings\r
466 extern int fUseProxy;\r
467 extern CAddress addrProxy;\r
468 \r
469 \r
470 \r
471 \r
472 \r
473 class CNode\r
474 {\r
475 public:\r
476     // socket\r
477     uint64 nServices;\r
478     SOCKET hSocket;\r
479     CDataStream vSend;\r
480     CDataStream vRecv;\r
481     CCriticalSection cs_vSend;\r
482     CCriticalSection cs_vRecv;\r
483     int64 nLastSend;\r
484     int64 nLastRecv;\r
485     int64 nLastSendEmpty;\r
486     int64 nTimeConnected;\r
487     unsigned int nPushPos;\r
488     CAddress addr;\r
489     int nVersion;\r
490     bool fClient;\r
491     bool fInbound;\r
492     bool fNetworkNode;\r
493     bool fSuccessfullyConnected;\r
494     bool fDisconnect;\r
495 protected:\r
496     int nRefCount;\r
497 public:\r
498     int64 nReleaseTime;\r
499     map<uint256, CRequestTracker> mapRequests;\r
500     CCriticalSection cs_mapRequests;\r
501     uint256 hashContinue;\r
502     CBlockIndex* pindexLastGetBlocksBegin;\r
503     uint256 hashLastGetBlocksEnd;\r
504 \r
505     // flood\r
506     vector<CAddress> vAddrToSend;\r
507     set<CAddress> setAddrKnown;\r
508     bool fGetAddr;\r
509 \r
510     // inventory based relay\r
511     set<CInv> setInventoryKnown;\r
512     vector<CInv> vInventoryToSend;\r
513     CCriticalSection cs_inventory;\r
514     multimap<int64, CInv> mapAskFor;\r
515     int64 nLastSentTxInv;\r
516 \r
517     // publish and subscription\r
518     vector<char> vfSubscribe;\r
519 \r
520 \r
521     CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)\r
522     {\r
523         nServices = 0;\r
524         hSocket = hSocketIn;\r
525         vSend.SetType(SER_NETWORK);\r
526         vRecv.SetType(SER_NETWORK);\r
527         nLastSend = 0;\r
528         nLastRecv = 0;\r
529         nLastSendEmpty = GetTime();\r
530         nTimeConnected = GetTime();\r
531         nPushPos = -1;\r
532         addr = addrIn;\r
533         nVersion = 0;\r
534         fClient = false; // set by version message\r
535         fInbound = fInboundIn;\r
536         fNetworkNode = false;\r
537         fSuccessfullyConnected = false;\r
538         fDisconnect = false;\r
539         nRefCount = 0;\r
540         nReleaseTime = 0;\r
541         hashContinue = 0;\r
542         pindexLastGetBlocksBegin = 0;\r
543         hashLastGetBlocksEnd = 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 \r
639     void BeginMessage(const char* pszCommand)\r
640     {\r
641         cs_vSend.Enter();\r
642         if (nPushPos != -1)\r
643             AbortMessage();\r
644         nPushPos = vSend.size();\r
645         vSend << CMessageHeader(pszCommand, 0);\r
646         if (fDebug)\r
647             printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());\r
648         printf("sending: %s ", pszCommand);\r
649     }\r
650 \r
651     void AbortMessage()\r
652     {\r
653         if (nPushPos == -1)\r
654             return;\r
655         vSend.resize(nPushPos);\r
656         nPushPos = -1;\r
657         cs_vSend.Leave();\r
658         printf("(aborted)\n");\r
659     }\r
660 \r
661     void EndMessage()\r
662     {\r
663         if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)\r
664         {\r
665             printf("dropmessages DROPPING SEND MESSAGE\n");\r
666             AbortMessage();\r
667             return;\r
668         }\r
669 \r
670         if (nPushPos == -1)\r
671             return;\r
672 \r
673         // Patch in the size\r
674         unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
675         memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));\r
676 \r
677         printf("(%d bytes) ", nSize);\r
678         printf("\n");\r
679 \r
680         nPushPos = -1;\r
681         cs_vSend.Leave();\r
682     }\r
683 \r
684     void EndMessageAbortIfEmpty()\r
685     {\r
686         if (nPushPos == -1)\r
687             return;\r
688         int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader);\r
689         if (nSize > 0)\r
690             EndMessage();\r
691         else\r
692             AbortMessage();\r
693     }\r
694 \r
695     const char* GetMessageCommand() const\r
696     {\r
697         if (nPushPos == -1)\r
698             return "";\r
699         return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand);\r
700     }\r
701 \r
702 \r
703 \r
704 \r
705     void PushMessage(const char* pszCommand)\r
706     {\r
707         try\r
708         {\r
709             BeginMessage(pszCommand);\r
710             EndMessage();\r
711         }\r
712         catch (...)\r
713         {\r
714             AbortMessage();\r
715             throw;\r
716         }\r
717     }\r
718 \r
719     template<typename T1>\r
720     void PushMessage(const char* pszCommand, const T1& a1)\r
721     {\r
722         try\r
723         {\r
724             BeginMessage(pszCommand);\r
725             vSend << a1;\r
726             EndMessage();\r
727         }\r
728         catch (...)\r
729         {\r
730             AbortMessage();\r
731             throw;\r
732         }\r
733     }\r
734 \r
735     template<typename T1, typename T2>\r
736     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)\r
737     {\r
738         try\r
739         {\r
740             BeginMessage(pszCommand);\r
741             vSend << a1 << a2;\r
742             EndMessage();\r
743         }\r
744         catch (...)\r
745         {\r
746             AbortMessage();\r
747             throw;\r
748         }\r
749     }\r
750 \r
751     template<typename T1, typename T2, typename T3>\r
752     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)\r
753     {\r
754         try\r
755         {\r
756             BeginMessage(pszCommand);\r
757             vSend << a1 << a2 << a3;\r
758             EndMessage();\r
759         }\r
760         catch (...)\r
761         {\r
762             AbortMessage();\r
763             throw;\r
764         }\r
765     }\r
766 \r
767     template<typename T1, typename T2, typename T3, typename T4>\r
768     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)\r
769     {\r
770         try\r
771         {\r
772             BeginMessage(pszCommand);\r
773             vSend << a1 << a2 << a3 << a4;\r
774             EndMessage();\r
775         }\r
776         catch (...)\r
777         {\r
778             AbortMessage();\r
779             throw;\r
780         }\r
781     }\r
782 \r
783     template<typename T1, typename T2, typename T3, typename T4, typename T5>\r
784     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)\r
785     {\r
786         try\r
787         {\r
788             BeginMessage(pszCommand);\r
789             vSend << a1 << a2 << a3 << a4 << a5;\r
790             EndMessage();\r
791         }\r
792         catch (...)\r
793         {\r
794             AbortMessage();\r
795             throw;\r
796         }\r
797     }\r
798 \r
799     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>\r
800     void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)\r
801     {\r
802         try\r
803         {\r
804             BeginMessage(pszCommand);\r
805             vSend << a1 << a2 << a3 << a4 << a5 << a6;\r
806             EndMessage();\r
807         }\r
808         catch (...)\r
809         {\r
810             AbortMessage();\r
811             throw;\r
812         }\r
813     }\r
814 \r
815     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>\r
816     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
817     {\r
818         try\r
819         {\r
820             BeginMessage(pszCommand);\r
821             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;\r
822             EndMessage();\r
823         }\r
824         catch (...)\r
825         {\r
826             AbortMessage();\r
827             throw;\r
828         }\r
829     }\r
830 \r
831     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>\r
832     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
833     {\r
834         try\r
835         {\r
836             BeginMessage(pszCommand);\r
837             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;\r
838             EndMessage();\r
839         }\r
840         catch (...)\r
841         {\r
842             AbortMessage();\r
843             throw;\r
844         }\r
845     }\r
846 \r
847     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>\r
848     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
849     {\r
850         try\r
851         {\r
852             BeginMessage(pszCommand);\r
853             vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;\r
854             EndMessage();\r
855         }\r
856         catch (...)\r
857         {\r
858             AbortMessage();\r
859             throw;\r
860         }\r
861     }\r
862 \r
863 \r
864     void PushRequest(const char* pszCommand,\r
865                      void (*fn)(void*, CDataStream&), void* param1)\r
866     {\r
867         uint256 hashReply;\r
868         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
869 \r
870         CRITICAL_BLOCK(cs_mapRequests)\r
871             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
872 \r
873         PushMessage(pszCommand, hashReply);\r
874     }\r
875 \r
876     template<typename T1>\r
877     void PushRequest(const char* pszCommand, const T1& a1,\r
878                      void (*fn)(void*, CDataStream&), void* param1)\r
879     {\r
880         uint256 hashReply;\r
881         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
882 \r
883         CRITICAL_BLOCK(cs_mapRequests)\r
884             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
885 \r
886         PushMessage(pszCommand, hashReply, a1);\r
887     }\r
888 \r
889     template<typename T1, typename T2>\r
890     void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,\r
891                      void (*fn)(void*, CDataStream&), void* param1)\r
892     {\r
893         uint256 hashReply;\r
894         RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));\r
895 \r
896         CRITICAL_BLOCK(cs_mapRequests)\r
897             mapRequests[hashReply] = CRequestTracker(fn, param1);\r
898 \r
899         PushMessage(pszCommand, hashReply, a1, a2);\r
900     }\r
901 \r
902 \r
903 \r
904     void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);\r
905     bool IsSubscribed(unsigned int nChannel);\r
906     void Subscribe(unsigned int nChannel, unsigned int nHops=0);\r
907     void CancelSubscribe(unsigned int nChannel);\r
908     void CloseSocketDisconnect();\r
909     void Cleanup();\r
910 };\r
911 \r
912 \r
913 \r
914 \r
915 \r
916 \r
917 \r
918 \r
919 \r
920 \r
921 inline void RelayInventory(const CInv& inv)\r
922 {\r
923     // Put on lists to offer to the other nodes\r
924     CRITICAL_BLOCK(cs_vNodes)\r
925         foreach(CNode* pnode, vNodes)\r
926             pnode->PushInventory(inv);\r
927 }\r
928 \r
929 template<typename T>\r
930 void RelayMessage(const CInv& inv, const T& a)\r
931 {\r
932     CDataStream ss(SER_NETWORK);\r
933     ss.reserve(10000);\r
934     ss << a;\r
935     RelayMessage(inv, ss);\r
936 }\r
937 \r
938 template<>\r
939 inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)\r
940 {\r
941     CRITICAL_BLOCK(cs_mapRelay)\r
942     {\r
943         // Expire old relay messages\r
944         while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())\r
945         {\r
946             mapRelay.erase(vRelayExpiration.front().second);\r
947             vRelayExpiration.pop_front();\r
948         }\r
949 \r
950         // Save original serialized message so newer versions are preserved\r
951         mapRelay[inv] = ss;\r
952         vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));\r
953     }\r
954 \r
955     RelayInventory(inv);\r
956 }\r
957 \r
958 \r
959 \r
960 \r
961 \r
962 \r
963 \r
964 \r
965 //\r
966 // Templates for the publish and subscription system.\r
967 // The object being published as T& obj needs to have:\r
968 //   a set<unsigned int> setSources member\r
969 //   specializations of AdvertInsert and AdvertErase\r
970 // Currently implemented for CTable and CProduct.\r
971 //\r
972 \r
973 template<typename T>\r
974 void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
975 {\r
976     // Add to sources\r
977     obj.setSources.insert(pfrom->addr.ip);\r
978 \r
979     if (!AdvertInsert(obj))\r
980         return;\r
981 \r
982     // Relay\r
983     CRITICAL_BLOCK(cs_vNodes)\r
984         foreach(CNode* pnode, vNodes)\r
985             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
986                 pnode->PushMessage("publish", nChannel, nHops, obj);\r
987 }\r
988 \r
989 template<typename T>\r
990 void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
991 {\r
992     uint256 hash = obj.GetHash();\r
993 \r
994     CRITICAL_BLOCK(cs_vNodes)\r
995         foreach(CNode* pnode, vNodes)\r
996             if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))\r
997                 pnode->PushMessage("pub-cancel", nChannel, nHops, hash);\r
998 \r
999     AdvertErase(obj);\r
1000 }\r
1001 \r
1002 template<typename T>\r
1003 void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)\r
1004 {\r
1005     // Remove a source\r
1006     obj.setSources.erase(pfrom->addr.ip);\r
1007 \r
1008     // If no longer supported by any sources, cancel it\r
1009     if (obj.setSources.empty())\r
1010         AdvertStopPublish(pfrom, nChannel, nHops, obj);\r
1011 }\r