diff options
| author | bitflash-coin <bookofshamers@outlook.com> | 2026-08-04 06:34:32 -0700 |
|---|---|---|
| committer | bitflash-coin <bookofshamers@outlook.com> | 2026-08-04 06:34:32 -0700 |
| commit | 5ce650382352c1503f40abf08129ada69402af55 (patch) | |
| tree | 6a18198901f1e2c4933f1ac53c9365eec512bf63 /src/net.h | |
| parent | f34113291da7e5e3dc7d2468d4f9c212c05c584b (diff) | |
public commit
Diffstat (limited to 'src/net.h')
| -rw-r--r-- | src/net.h | 1100 |
1 files changed, 1100 insertions, 0 deletions
diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..bcd0b2a --- /dev/null +++ b/src/net.h | |||
| @@ -0,0 +1,1100 @@ | |||
| 1 | // Copyright (c) 2009 Satoshi Nakamoto | ||
| 2 | // Distributed under the MIT/X11 software license, see the accompanying | ||
| 3 | // file license.txt or http://www.opensource.org/licenses/mit-license.php. | ||
| 4 | |||
| 5 | class CMessageHeader; | ||
| 6 | class CAddress; | ||
| 7 | class CInv; | ||
| 8 | class CRequestTracker; | ||
| 9 | class CNode; | ||
| 10 | // Defined in main.h, which is included after this file. Only ever used here as | ||
| 11 | // a pointer, so the forward declaration is enough. | ||
| 12 | class CBlockIndex; | ||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | static const unsigned short DEFAULT_PORT = htons(8433); | ||
| 17 | static const unsigned int PUBLISH_HOPS = 5; | ||
| 18 | enum | ||
| 19 | { | ||
| 20 | NODE_NETWORK = (1 << 0), | ||
| 21 | }; | ||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | bool GetMyExternalIP(unsigned int& ipRet); | ||
| 29 | CNode* ConnectNodeBtf(const string& strBtfAddr); | ||
| 30 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, | ||
| 31 | const unsigned char enc_pub[32]); | ||
| 32 | void ThreadBtfAccept(void* parg); | ||
| 33 | void ThreadBtfConnect(void* parg); | ||
| 34 | extern string strBtfConnect; | ||
| 35 | // Bootstrap seeds added at runtime with /btfseed=ADDRESS:ENCHEX (repeatable). | ||
| 36 | // The compiled-in list lives in net.cpp; these are appended to it. | ||
| 37 | extern std::vector<std::pair<std::string, std::string> > vBtfExtraSeeds; | ||
| 38 | |||
| 39 | // --- .btf peer exchange --------------------------------------------------- | ||
| 40 | // Peers gossip signed descriptors so discovery survives a relay outage. Both | ||
| 41 | // caps apply to a single "btfpeers" message; the generic header limit is 256 MB, | ||
| 42 | // which is no protection at all for something a stranger can send unprompted. | ||
| 43 | static const unsigned int MAX_PEX_DESCRIPTORS = 20; | ||
| 44 | static const unsigned int MAX_PEX_DESCRIPTOR_BYTES = 1024; | ||
| 45 | // Ignore a peer's exchange more often than this. One announcement per side per | ||
| 46 | // connection is the intended traffic; anything faster is someone else's idea. | ||
| 47 | static const int64 PEX_MIN_INTERVAL = 60; | ||
| 48 | |||
| 49 | // How long to sit registered at a rendezvous relay before giving up on this | ||
| 50 | // attempt and registering again. Bounded because a registration socket that | ||
| 51 | // goes quiet is indistinguishable, from inside recv(), from one whose path has | ||
| 52 | // died -- and the second kind never wakes up. Five minutes is short enough | ||
| 53 | // that a node is unreachable only briefly and long enough that the reconnect | ||
| 54 | // traffic is nothing: one per relay per five minutes. | ||
| 55 | static const int BTF_RENDEZVOUS_WAIT_SECS = 300; | ||
| 56 | |||
| 57 | // Registrations this node parks at a rendezvous at once. | ||
| 58 | // | ||
| 59 | // Back to one, and it stays at one until every relay in vBtfMeetingRelays runs | ||
| 60 | // the queue from #113. More than one is what keeps a node reachable while one | ||
| 61 | // registration is being paired -- but only against a relay that accepts more | ||
| 62 | // than one. An older relay closes the previous registration whenever the same | ||
| 63 | // node registers again, so several accept threads pointed at one of those | ||
| 64 | // evict each other in a loop: thread 2 registers and kills thread 1, thread 3 | ||
| 65 | // kills thread 2, thread 1 sees its socket die and kills thread 3. | ||
| 66 | // | ||
| 67 | // #113 called that harmless. It is not. Measured on a node running three | ||
| 68 | // threads against the un-upgraded New Jersey relay: 189 registrations in about | ||
| 69 | // fifteen minutes -- roughly twelve a minute -- and not one pairing. The node | ||
| 70 | // was not merely failing to benefit, it was unreachable through that relay and | ||
| 71 | // hammering it. A node with one thread does none of this. | ||
| 72 | // | ||
| 73 | // The bench that approved #113 could not see it: its harness re-registers by | ||
| 74 | // design, which is exactly what hides an eviction loop. | ||
| 75 | // | ||
| 76 | // Raise this to 3 only when the other two relays are upgraded. | ||
| 77 | static const int BTF_ACCEPT_THREADS = 1; | ||
| 78 | |||
| 79 | // Liveness of an established peer connection. BTF_RENDEZVOUS_WAIT_SECS bounds | ||
| 80 | // the wait for a dial to arrive; these bound the connection that comes out of | ||
| 81 | // it, which has the same failure mode once it goes quiet. | ||
| 82 | // | ||
| 83 | // A healthy peer relays a block inventory roughly every nTargetSpacing (two | ||
| 84 | // minutes), so silence measured in block intervals is the honest signal. | ||
| 85 | // Bitcoin used ninety minutes against ten-minute blocks -- nine intervals -- | ||
| 86 | // and the same nine intervals here is eighteen minutes; thirty leaves margin | ||
| 87 | // for a slow stretch without letting a dead path last an hour. | ||
| 88 | static const int BTF_RECV_TIMEOUT_SECS = 30 * 60; | ||
| 89 | static const int BTF_SEND_STALL_SECS = 10 * 60; | ||
| 90 | static const int BTF_HANDSHAKE_GRACE_SECS = 60; | ||
| 91 | |||
| 92 | // A serialized object cannot be larger than MAX_SIZE, so no valid network | ||
| 93 | // message needs the old 256 MB header allowance. Keep the wire cap aligned with | ||
| 94 | // what the deserializer can accept and bound how long one peer may sit on a | ||
| 95 | // partial payload after a complete header has arrived. | ||
| 96 | static const unsigned int MAX_PROTOCOL_MESSAGE_SIZE = MAX_SIZE; | ||
| 97 | static const int BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS = 2 * 60; | ||
| 98 | |||
| 99 | // Ceiling on simultaneous connections. | ||
| 100 | // | ||
| 101 | // ThreadSocketHandler watches every peer through one select(), and select() | ||
| 102 | // cannot watch more descriptors than FD_SETSIZE. On Windows FD_SET simply | ||
| 103 | // stops adding once the set is full -- no error, no log -- so every socket | ||
| 104 | // past the limit stays open and is never read again. Nothing capped vNodes, | ||
| 105 | // so a node accepted connections it had no way to service, and because new | ||
| 106 | // entries go on the end of vNodes it was always the newest connection that | ||
| 107 | // starved, including the ones dialed to fix connectivity. | ||
| 108 | // | ||
| 109 | // Measured on two nodes before this ceiling existed: 277 and 90 CNode objects | ||
| 110 | // against 93 and 90 live sockets, and a status bar reporting hundreds of | ||
| 111 | // peers for a node that was really talking to thirty. | ||
| 112 | // | ||
| 113 | // 125 is Bitcoin's number and sits far below FD_SETSIZE on both platforms. | ||
| 114 | static const unsigned int MAX_CONNECTIONS = 125; | ||
| 115 | |||
| 116 | // A node marked for disconnect is normally held until its buffers drain, so | ||
| 117 | // a last message still goes out. But vSend cannot drain through a socket | ||
| 118 | // whose far end is gone, and that made such a node immortal: it stayed in | ||
| 119 | // vNodes, kept its socket open, and kept being counted. Force it out after | ||
| 120 | // this long regardless of what is still buffered. | ||
| 121 | static const int64 DISCONNECT_DRAIN_SECS = 60; | ||
| 122 | |||
| 123 | // Send a ping after this long with nothing to say, so a peer running the | ||
| 124 | // inactivity check above does not mistake a quiet node for a dead one. Must | ||
| 125 | // stay well under BTF_RECV_TIMEOUT_SECS. | ||
| 126 | static const int BTF_PING_INTERVAL_SECS = 10 * 60; | ||
| 127 | |||
| 128 | // How long a node must stay behind the network before it says so, and how | ||
| 129 | // often it repeats itself. Five minutes is two and a half block intervals -- | ||
| 130 | // long enough that ordinary relay lag never trips it. | ||
| 131 | static const int BTF_BEHIND_GRACE_SECS = 5 * 60; | ||
| 132 | static const int BTF_BEHIND_WARN_INTERVAL_SECS = 5 * 60; | ||
| 133 | |||
| 134 | // --- Self-diagnosis ------------------------------------------------------- | ||
| 135 | // | ||
| 136 | // A node that has stopped working looks exactly like a node with nothing to | ||
| 137 | // do. That has cost this project real time more than once: a node deaf to | ||
| 138 | // most of its peers, a miner allocating one RandomX dataset per thread, a | ||
| 139 | // third of arriving blocks missing their parent -- each was found from | ||
| 140 | // outside, by reading the process's sockets or its memory from another | ||
| 141 | // machine, because the node itself had no way to say so. | ||
| 142 | // | ||
| 143 | // These counters exist so it can. Cheap to keep, and they name the failures | ||
| 144 | // that actually happened rather than the ones that sound impressive. | ||
| 145 | extern int nPeersWatched; // peers in the last select() set | ||
| 146 | extern int64 nBlocksReceived; // blocks handed to ProcessBlock | ||
| 147 | extern int64 nBlocksWithoutParent; // ...of those, how many arrived orphaned | ||
| 148 | |||
| 149 | // One report, rendered the same way for the GUI panel and the log, so what a | ||
| 150 | // user pastes into an issue is what a developer already knows how to read. | ||
| 151 | string GetDiagnosticsText(); | ||
| 152 | |||
| 153 | #include "sockcount.h" | ||
| 154 | |||
| 155 | // Defined in main.cpp. Declared here because CNode announces it in the version | ||
| 156 | // message, and net.h is included before main.h. | ||
| 157 | extern int nBestHeight; | ||
| 158 | |||
| 159 | // Median height announced by the peers we are connected to, or -1 while nobody | ||
| 160 | // has told us anything. Median rather than maximum so one peer claiming an | ||
| 161 | // absurd height cannot move it. | ||
| 162 | int GetPeerMedianHeight(); | ||
| 163 | |||
| 164 | // Lightweight .btf churn accounting. These counters are diagnostics only: they | ||
| 165 | // let an operator tell stale descriptors, relay pairing failures, and paired | ||
| 166 | // tunnels that never speak apart before changing behaviour. | ||
| 167 | void BtfChurnNoteResolveAttempt(); | ||
| 168 | void BtfChurnNoteResolveResult(bool fOk); | ||
| 169 | void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting); | ||
| 170 | void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk); | ||
| 171 | void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk); | ||
| 172 | void BtfChurnNotePairResult(const string& strMeeting, bool fOk); | ||
| 173 | void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting, | ||
| 174 | bool fRecv, bool fSend); | ||
| 175 | |||
| 176 | // Descriptors to hand a peer: ours first, then peers that answered us. | ||
| 177 | void BtfPexCollect(std::vector<std::string>& vDescOut); | ||
| 178 | // Verify descriptors a peer sent and remember the good ones. Returns how many. | ||
| 179 | int BtfPexAccept(const std::vector<std::string>& vDesc); | ||
| 180 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); | ||
| 181 | bool AnySubscribed(unsigned int nChannel); | ||
| 182 | void ThreadBitcoinMiner(void* parg); | ||
| 183 | // Starts MinerThreadCount() miners; returns how many came up. | ||
| 184 | int StartMinerThreads(); | ||
| 185 | bool StartNode(string& strError=REF(string())); | ||
| 186 | bool StopNode(); | ||
| 187 | void CheckForShutdown(int n); | ||
| 188 | |||
| 189 | |||
| 190 | |||
| 191 | |||
| 192 | |||
| 193 | |||
| 194 | |||
| 195 | |||
| 196 | |||
| 197 | // | ||
| 198 | // Message header | ||
| 199 | // (4) message start | ||
| 200 | // (12) command | ||
| 201 | // (4) size | ||
| 202 | |||
| 203 | // The message start string is designed to be unlikely to occur in normal data. | ||
| 204 | // The characters are rarely used upper ascii, not valid as UTF-8, and produce | ||
| 205 | // a large 4-byte int at any alignment. | ||
| 206 | // Magic bytes unique to the Bitflash network (0xbf = "BF"). Second byte bumped | ||
| 207 | // to 0x20 for the 2026 stable relaunch so the old test chain can't interfere. | ||
| 208 | static const char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | ||
| 209 | |||
| 210 | class CMessageHeader | ||
| 211 | { | ||
| 212 | public: | ||
| 213 | enum { COMMAND_SIZE=12 }; | ||
| 214 | char pchMessageStart[sizeof(::pchMessageStart)]; | ||
| 215 | char pchCommand[COMMAND_SIZE]; | ||
| 216 | unsigned int nMessageSize; | ||
| 217 | |||
| 218 | CMessageHeader() | ||
| 219 | { | ||
| 220 | memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); | ||
| 221 | memset(pchCommand, 0, sizeof(pchCommand)); | ||
| 222 | pchCommand[1] = 1; | ||
| 223 | nMessageSize = -1; | ||
| 224 | } | ||
| 225 | |||
| 226 | CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) | ||
| 227 | { | ||
| 228 | memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); | ||
| 229 | strncpy(pchCommand, pszCommand, COMMAND_SIZE); | ||
| 230 | nMessageSize = nMessageSizeIn; | ||
| 231 | } | ||
| 232 | |||
| 233 | IMPLEMENT_SERIALIZE | ||
| 234 | ( | ||
| 235 | READWRITE(FLATDATA(pchMessageStart)); | ||
| 236 | READWRITE(FLATDATA(pchCommand)); | ||
| 237 | READWRITE(nMessageSize); | ||
| 238 | ) | ||
| 239 | |||
| 240 | string GetCommand() | ||
| 241 | { | ||
| 242 | if (pchCommand[COMMAND_SIZE-1] == 0) | ||
| 243 | return string(pchCommand, pchCommand + strlen(pchCommand)); | ||
| 244 | else | ||
| 245 | return string(pchCommand, pchCommand + COMMAND_SIZE); | ||
| 246 | } | ||
| 247 | |||
| 248 | bool IsValid() | ||
| 249 | { | ||
| 250 | // Check start string | ||
| 251 | if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) | ||
| 252 | return false; | ||
| 253 | |||
| 254 | // Check the command string for errors | ||
| 255 | for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) | ||
| 256 | { | ||
| 257 | if (*p1 == 0) | ||
| 258 | { | ||
| 259 | // Must be all zeros after the first zero | ||
| 260 | for (; p1 < pchCommand + COMMAND_SIZE; p1++) | ||
| 261 | if (*p1 != 0) | ||
| 262 | return false; | ||
| 263 | } | ||
| 264 | else if (*p1 < ' ' || *p1 > 0x7E) | ||
| 265 | return false; | ||
| 266 | } | ||
| 267 | |||
| 268 | // Message size | ||
| 269 | if (nMessageSize > MAX_PROTOCOL_MESSAGE_SIZE) | ||
| 270 | { | ||
| 271 | if (LogAcceptsCategory("net")) printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize); | ||
| 272 | return false; | ||
| 273 | } | ||
| 274 | |||
| 275 | return true; | ||
| 276 | } | ||
| 277 | }; | ||
| 278 | |||
| 279 | |||
| 280 | |||
| 281 | |||
| 282 | |||
| 283 | |||
| 284 | static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; | ||
| 285 | |||
| 286 | class CAddress | ||
| 287 | { | ||
| 288 | public: | ||
| 289 | uint64 nServices; | ||
| 290 | unsigned char pchReserved[12]; | ||
| 291 | unsigned int ip; | ||
| 292 | unsigned short port; | ||
| 293 | |||
| 294 | // disk only | ||
| 295 | unsigned int nTime; | ||
| 296 | |||
| 297 | // memory only | ||
| 298 | unsigned int nLastFailed; | ||
| 299 | |||
| 300 | CAddress() | ||
| 301 | { | ||
| 302 | nServices = 0; | ||
| 303 | memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); | ||
| 304 | ip = 0; | ||
| 305 | port = DEFAULT_PORT; | ||
| 306 | nTime = GetAdjustedTime(); | ||
| 307 | nLastFailed = 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | CAddress(unsigned int ipIn, unsigned short portIn, uint64 nServicesIn=0) | ||
| 311 | { | ||
| 312 | nServices = nServicesIn; | ||
| 313 | memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); | ||
| 314 | ip = ipIn; | ||
| 315 | port = portIn; | ||
| 316 | nTime = GetAdjustedTime(); | ||
| 317 | nLastFailed = 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=0) | ||
| 321 | { | ||
| 322 | nServices = nServicesIn; | ||
| 323 | memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); | ||
| 324 | ip = sockaddr.sin_addr.s_addr; | ||
| 325 | port = sockaddr.sin_port; | ||
| 326 | nTime = GetAdjustedTime(); | ||
| 327 | nLastFailed = 0; | ||
| 328 | } | ||
| 329 | |||
| 330 | explicit CAddress(const char* pszIn, uint64 nServicesIn=0) | ||
| 331 | { | ||
| 332 | nServices = nServicesIn; | ||
| 333 | memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); | ||
| 334 | ip = 0; | ||
| 335 | port = DEFAULT_PORT; | ||
| 336 | nTime = GetAdjustedTime(); | ||
| 337 | nLastFailed = 0; | ||
| 338 | |||
| 339 | char psz[100]; | ||
| 340 | if (strlen(pszIn) > ARRAYLEN(psz)-1) | ||
| 341 | return; | ||
| 342 | strcpy(psz, pszIn); | ||
| 343 | unsigned int a, b, c, d, e; | ||
| 344 | if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4) | ||
| 345 | return; | ||
| 346 | char* pszPort = strchr(psz, ':'); | ||
| 347 | if (pszPort) | ||
| 348 | { | ||
| 349 | *pszPort++ = '\0'; | ||
| 350 | port = htons(atoi(pszPort)); | ||
| 351 | } | ||
| 352 | ip = inet_addr(psz); | ||
| 353 | } | ||
| 354 | |||
| 355 | IMPLEMENT_SERIALIZE | ||
| 356 | ( | ||
| 357 | if (nType & SER_DISK) | ||
| 358 | { | ||
| 359 | READWRITE(nVersion); | ||
| 360 | READWRITE(nTime); | ||
| 361 | } | ||
| 362 | READWRITE(nServices); | ||
| 363 | READWRITE(FLATDATA(pchReserved)); | ||
| 364 | READWRITE(ip); | ||
| 365 | READWRITE(port); | ||
| 366 | ) | ||
| 367 | |||
| 368 | friend inline bool operator==(const CAddress& a, const CAddress& b) | ||
| 369 | { | ||
| 370 | return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 && | ||
| 371 | a.ip == b.ip && | ||
| 372 | a.port == b.port); | ||
| 373 | } | ||
| 374 | |||
| 375 | friend inline bool operator<(const CAddress& a, const CAddress& b) | ||
| 376 | { | ||
| 377 | int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)); | ||
| 378 | if (ret < 0) | ||
| 379 | return true; | ||
| 380 | else if (ret == 0) | ||
| 381 | { | ||
| 382 | if (ntohl(a.ip) < ntohl(b.ip)) | ||
| 383 | return true; | ||
| 384 | else if (a.ip == b.ip) | ||
| 385 | return ntohs(a.port) < ntohs(b.port); | ||
| 386 | } | ||
| 387 | return false; | ||
| 388 | } | ||
| 389 | |||
| 390 | vector<unsigned char> GetKey() const | ||
| 391 | { | ||
| 392 | CDataStream ss; | ||
| 393 | ss.reserve(18); | ||
| 394 | ss << FLATDATA(pchReserved) << ip << port; | ||
| 395 | |||
| 396 | #if defined(_MSC_VER) && _MSC_VER < 1300 | ||
| 397 | return vector<unsigned char>((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]); | ||
| 398 | #else | ||
| 399 | return vector<unsigned char>(ss.begin(), ss.end()); | ||
| 400 | #endif | ||
| 401 | } | ||
| 402 | |||
| 403 | struct sockaddr_in GetSockAddr() const | ||
| 404 | { | ||
| 405 | struct sockaddr_in sockaddr; | ||
| 406 | sockaddr.sin_family = AF_INET; | ||
| 407 | sockaddr.sin_addr.s_addr = ip; | ||
| 408 | sockaddr.sin_port = port; | ||
| 409 | return sockaddr; | ||
| 410 | } | ||
| 411 | |||
| 412 | bool IsIPv4() const | ||
| 413 | { | ||
| 414 | return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); | ||
| 415 | } | ||
| 416 | |||
| 417 | bool IsRoutable() const | ||
| 418 | { | ||
| 419 | return !(GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168)); | ||
| 420 | } | ||
| 421 | |||
| 422 | unsigned char GetByte(int n) const | ||
| 423 | { | ||
| 424 | return ((unsigned char*)&ip)[3-n]; | ||
| 425 | } | ||
| 426 | |||
| 427 | string ToStringIPPort() const | ||
| 428 | { | ||
| 429 | return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); | ||
| 430 | } | ||
| 431 | |||
| 432 | string ToStringIP() const | ||
| 433 | { | ||
| 434 | return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); | ||
| 435 | } | ||
| 436 | |||
| 437 | string ToString() const | ||
| 438 | { | ||
| 439 | return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); | ||
| 440 | //return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); | ||
| 441 | } | ||
| 442 | |||
| 443 | void print() const | ||
| 444 | { | ||
| 445 | if (LogAcceptsCategory("net")) printf("CAddress(%s)\n", ToString().c_str()); | ||
| 446 | } | ||
| 447 | }; | ||
| 448 | |||
| 449 | |||
| 450 | |||
| 451 | |||
| 452 | |||
| 453 | |||
| 454 | |||
| 455 | enum | ||
| 456 | { | ||
| 457 | MSG_TX = 1, | ||
| 458 | MSG_BLOCK, | ||
| 459 | MSG_REVIEW, | ||
| 460 | MSG_PRODUCT, | ||
| 461 | MSG_TABLE, | ||
| 462 | }; | ||
| 463 | |||
| 464 | static const char* ppszTypeName[] = | ||
| 465 | { | ||
| 466 | "ERROR", | ||
| 467 | "tx", | ||
| 468 | "block", | ||
| 469 | "review", | ||
| 470 | "product", | ||
| 471 | "table", | ||
| 472 | }; | ||
| 473 | |||
| 474 | class CInv | ||
| 475 | { | ||
| 476 | public: | ||
| 477 | int type; | ||
| 478 | uint256 hash; | ||
| 479 | |||
| 480 | CInv() | ||
| 481 | { | ||
| 482 | type = 0; | ||
| 483 | hash = 0; | ||
| 484 | } | ||
| 485 | |||
| 486 | CInv(int typeIn, const uint256& hashIn) | ||
| 487 | { | ||
| 488 | type = typeIn; | ||
| 489 | hash = hashIn; | ||
| 490 | } | ||
| 491 | |||
| 492 | CInv(const string& strType, const uint256& hashIn) | ||
| 493 | { | ||
| 494 | int i; | ||
| 495 | for (i = 1; i < ARRAYLEN(ppszTypeName); i++) | ||
| 496 | { | ||
| 497 | if (strType == ppszTypeName[i]) | ||
| 498 | { | ||
| 499 | type = i; | ||
| 500 | break; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | if (i == ARRAYLEN(ppszTypeName)) | ||
| 504 | throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); | ||
| 505 | hash = hashIn; | ||
| 506 | } | ||
| 507 | |||
| 508 | IMPLEMENT_SERIALIZE | ||
| 509 | ( | ||
| 510 | READWRITE(type); | ||
| 511 | READWRITE(hash); | ||
| 512 | ) | ||
| 513 | |||
| 514 | friend inline bool operator<(const CInv& a, const CInv& b) | ||
| 515 | { | ||
| 516 | return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); | ||
| 517 | } | ||
| 518 | |||
| 519 | bool IsKnownType() const | ||
| 520 | { | ||
| 521 | return (type >= 1 && type < ARRAYLEN(ppszTypeName)); | ||
| 522 | } | ||
| 523 | |||
| 524 | const char* GetCommand() const | ||
| 525 | { | ||
| 526 | if (!IsKnownType()) | ||
| 527 | // Was "type=% unknown type": the % swallowed the space as a flag and | ||
| 528 | // the u as the conversion, so this read "type= 3nknown type". | ||
| 529 | throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); | ||
| 530 | return ppszTypeName[type]; | ||
| 531 | } | ||
| 532 | |||
| 533 | string ToString() const | ||
| 534 | { | ||
| 535 | return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,14).c_str()); | ||
| 536 | } | ||
| 537 | |||
| 538 | void print() const | ||
| 539 | { | ||
| 540 | if (LogAcceptsCategory("net")) printf("CInv(%s)\n", ToString().c_str()); | ||
| 541 | } | ||
| 542 | }; | ||
| 543 | |||
| 544 | |||
| 545 | |||
| 546 | |||
| 547 | |||
| 548 | class CRequestTracker | ||
| 549 | { | ||
| 550 | public: | ||
| 551 | void (*fn)(void*, CDataStream&); | ||
| 552 | void* param1; | ||
| 553 | |||
| 554 | explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) | ||
| 555 | { | ||
| 556 | fn = fnIn; | ||
| 557 | param1 = param1In; | ||
| 558 | } | ||
| 559 | |||
| 560 | bool IsNull() | ||
| 561 | { | ||
| 562 | return fn == NULL; | ||
| 563 | } | ||
| 564 | }; | ||
| 565 | |||
| 566 | |||
| 567 | |||
| 568 | |||
| 569 | |||
| 570 | extern bool fClient; | ||
| 571 | extern uint64 nLocalServices; | ||
| 572 | extern CAddress addrLocalHost; | ||
| 573 | extern unsigned short nListenPort; | ||
| 574 | extern CNode* pnodeLocalHost; | ||
| 575 | extern bool fShutdown; | ||
| 576 | extern array<bool, 10> vfThreadRunning; | ||
| 577 | extern vector<CNode*> vNodes; | ||
| 578 | extern CCriticalSection cs_vNodes; | ||
| 579 | extern map<CInv, CDataStream> mapRelay; | ||
| 580 | extern deque<pair<int64, CInv> > vRelayExpiration; | ||
| 581 | extern CCriticalSection cs_mapRelay; | ||
| 582 | extern map<CInv, int64> mapAlreadyAskedFor; | ||
| 583 | |||
| 584 | |||
| 585 | |||
| 586 | |||
| 587 | |||
| 588 | class CNode | ||
| 589 | { | ||
| 590 | public: | ||
| 591 | // socket | ||
| 592 | uint64 nServices; | ||
| 593 | SOCKET hSocket; | ||
| 594 | CDataStream vSend; | ||
| 595 | CDataStream vRecv; | ||
| 596 | CCriticalSection cs_vSend; | ||
| 597 | CCriticalSection cs_vRecv; | ||
| 598 | unsigned int nPushPos; | ||
| 599 | CAddress addr; | ||
| 600 | int nVersion; | ||
| 601 | bool fClient; | ||
| 602 | bool fInbound; | ||
| 603 | bool fNetworkNode; | ||
| 604 | bool fDisconnect; | ||
| 605 | protected: | ||
| 606 | int nRefCount; | ||
| 607 | public: | ||
| 608 | int64 nReleaseTime; | ||
| 609 | int64 nDisconnectSince; | ||
| 610 | map<uint256, CRequestTracker> mapRequests; | ||
| 611 | CCriticalSection cs_mapRequests; | ||
| 612 | |||
| 613 | // inventory based relay | ||
| 614 | set<CInv> setInventoryKnown; | ||
| 615 | set<CInv> setInventoryKnown2; | ||
| 616 | vector<CInv> vInventoryToSend; | ||
| 617 | CCriticalSection cs_inventory; | ||
| 618 | multimap<int64, CInv> mapAskFor; | ||
| 619 | |||
| 620 | // publish and subscription | ||
| 621 | vector<char> vfSubscribe; | ||
| 622 | |||
| 623 | // Last "btfpeers" we accepted from this node, to rate-limit the exchange. | ||
| 624 | int64 nLastPexRecv; | ||
| 625 | |||
| 626 | // Last getblocks we sent this peer, so we do not ask the same question | ||
| 627 | // over and over. See CNode::PushGetBlocks. | ||
| 628 | CBlockIndex* pindexLastGetBlocksBegin; | ||
| 629 | uint256 hashLastGetBlocksEnd; | ||
| 630 | |||
| 631 | // Liveness. A peer whose network path dies silently -- NAT drops an idle | ||
| 632 | // mapping, a relay restarts, a route changes -- never sends FIN, so the | ||
| 633 | // socket stays readable-never and the node simply stops hearing from it | ||
| 634 | // with no error anywhere. These stamps are the only way to tell that | ||
| 635 | // apart from a peer that merely has nothing to say. | ||
| 636 | int64 nTimeConnected; | ||
| 637 | int64 nLastSend; | ||
| 638 | int64 nLastRecv; | ||
| 639 | int64 nLastSendEmpty; | ||
| 640 | int64 nIncompleteMessageStart; | ||
| 641 | unsigned int nIncompleteMessageSize; | ||
| 642 | string strIncompleteMessageCommand; | ||
| 643 | string strBtfAddr; | ||
| 644 | string strBtfMeeting; | ||
| 645 | |||
| 646 | // Height this peer announced in its version message, or -1 if it sent a | ||
| 647 | // version message that predates the field. Knowing how far along everyone | ||
| 648 | // else is, is the only way a node can tell "in sync" from "not hearing". | ||
| 649 | int nStartingHeight; | ||
| 650 | |||
| 651 | |||
| 652 | CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) | ||
| 653 | { | ||
| 654 | nServices = 0; | ||
| 655 | hSocket = hSocketIn; | ||
| 656 | vSend.SetType(SER_NETWORK); | ||
| 657 | vRecv.SetType(SER_NETWORK); | ||
| 658 | nPushPos = -1; | ||
| 659 | addr = addrIn; | ||
| 660 | nVersion = 0; | ||
| 661 | fClient = false; // set by version message | ||
| 662 | fInbound = fInboundIn; | ||
| 663 | fNetworkNode = false; | ||
| 664 | fDisconnect = false; | ||
| 665 | nRefCount = 0; | ||
| 666 | nReleaseTime = 0; | ||
| 667 | nDisconnectSince = 0; | ||
| 668 | nLastPexRecv = 0; | ||
| 669 | pindexLastGetBlocksBegin = NULL; | ||
| 670 | hashLastGetBlocksEnd = 0; | ||
| 671 | nTimeConnected = GetTime(); | ||
| 672 | nLastSend = 0; | ||
| 673 | nLastRecv = 0; | ||
| 674 | nLastSendEmpty = GetTime(); | ||
| 675 | nIncompleteMessageStart = 0; | ||
| 676 | nIncompleteMessageSize = 0; | ||
| 677 | strIncompleteMessageCommand.clear(); | ||
| 678 | strBtfAddr.clear(); | ||
| 679 | strBtfMeeting.clear(); | ||
| 680 | nStartingHeight = -1; | ||
| 681 | vfSubscribe.assign(256, false); | ||
| 682 | |||
| 683 | // Push a version message | ||
| 684 | /// when NTP implemented, change to just nTime = GetAdjustedTime() | ||
| 685 | int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); | ||
| 686 | // nBestHeight goes last so a node that predates it stops reading after | ||
| 687 | // addr and treats the rest as trailing bytes, which cost it a log line | ||
| 688 | // and nothing else. | ||
| 689 | PushMessage("version", VERSION, nLocalServices, nTime, addr, nBestHeight); | ||
| 690 | } | ||
| 691 | |||
| 692 | // Disconnect() closes the socket and sets this to INVALID_SOCKET, so by the | ||
| 693 | // time a node is deleted there is normally nothing left to do here. It used | ||
| 694 | // not to: Disconnect() closed the handle and left the number in place, and | ||
| 695 | // this destructor closed that same number a second time -- with a raw | ||
| 696 | // closesocket() that the socket accounting never saw, so neither the double | ||
| 697 | // close nor the stale claim showed up in any report. | ||
| 698 | // | ||
| 699 | // A double close is not a harmless no-op. Between the two, the operating | ||
| 700 | // system is free to hand that number to a new socket -- this node opens | ||
| 701 | // hundreds a minute -- and the second close then takes down a connection | ||
| 702 | // that belongs to somebody else, which the log can only report as a peer | ||
| 703 | // vanishing for no reason. `dropping node: its socket is no longer valid` | ||
| 704 | // is what that looks like from the outside. | ||
| 705 | ~CNode() | ||
| 706 | { | ||
| 707 | if (hSocket != INVALID_SOCKET) | ||
| 708 | { | ||
| 709 | BtfCloseSocket(hSocket); | ||
| 710 | hSocket = INVALID_SOCKET; | ||
| 711 | } | ||
| 712 | } | ||
| 713 | |||
| 714 | private: | ||
| 715 | CNode(const CNode&); | ||
| 716 | void operator=(const CNode&); | ||
| 717 | public: | ||
| 718 | |||
| 719 | |||
| 720 | bool ReadyToDisconnect() | ||
| 721 | { | ||
| 722 | return fDisconnect || GetRefCount() <= 0; | ||
| 723 | } | ||
| 724 | |||
| 725 | int GetRefCount() | ||
| 726 | { | ||
| 727 | return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); | ||
| 728 | } | ||
| 729 | |||
| 730 | void AddRef(int64 nTimeout=0) | ||
| 731 | { | ||
| 732 | if (nTimeout != 0) | ||
| 733 | nReleaseTime = max(nReleaseTime, GetTime() + nTimeout); | ||
| 734 | else | ||
| 735 | nRefCount++; | ||
| 736 | } | ||
| 737 | |||
| 738 | void Release() | ||
| 739 | { | ||
| 740 | nRefCount--; | ||
| 741 | } | ||
| 742 | |||
| 743 | |||
| 744 | |||
| 745 | void AddInventoryKnown(const CInv& inv) | ||
| 746 | { | ||
| 747 | CRITICAL_BLOCK(cs_inventory) | ||
| 748 | setInventoryKnown.insert(inv); | ||
| 749 | } | ||
| 750 | |||
| 751 | void PushInventory(const CInv& inv) | ||
| 752 | { | ||
| 753 | CRITICAL_BLOCK(cs_inventory) | ||
| 754 | if (!setInventoryKnown.count(inv)) | ||
| 755 | vInventoryToSend.push_back(inv); | ||
| 756 | } | ||
| 757 | |||
| 758 | void AskFor(const CInv& inv) | ||
| 759 | { | ||
| 760 | // We're using mapAskFor as a priority queue, | ||
| 761 | // the key is the earliest time the request can be sent | ||
| 762 | int64& nRequestTime = mapAlreadyAskedFor[inv]; | ||
| 763 | if (LogAcceptsCategory("net")) printf("askfor %s %lld\n", inv.ToString().c_str(), nRequestTime); | ||
| 764 | |||
| 765 | // Make sure not to reuse time indexes to keep things in the same order | ||
| 766 | int64 nNow = (GetTime() - 1) * 1000000; | ||
| 767 | static int64 nLastTime; | ||
| 768 | nLastTime = nNow = max(nNow, ++nLastTime); | ||
| 769 | |||
| 770 | // Each retry is 2 minutes after the last | ||
| 771 | nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow); | ||
| 772 | mapAskFor.insert(make_pair(nRequestTime, inv)); | ||
| 773 | } | ||
| 774 | |||
| 775 | |||
| 776 | |||
| 777 | void BeginMessage(const char* pszCommand) | ||
| 778 | { | ||
| 779 | EnterCriticalSection(&cs_vSend); | ||
| 780 | if (nPushPos != -1) | ||
| 781 | AbortMessage(); | ||
| 782 | nPushPos = vSend.size(); | ||
| 783 | vSend << CMessageHeader(pszCommand, 0); | ||
| 784 | if (LogAcceptsCategory("net")) printf("sending: %-12s ", pszCommand); | ||
| 785 | } | ||
| 786 | |||
| 787 | void AbortMessage() | ||
| 788 | { | ||
| 789 | if (nPushPos == -1) | ||
| 790 | return; | ||
| 791 | vSend.resize(nPushPos); | ||
| 792 | nPushPos = -1; | ||
| 793 | LeaveCriticalSection(&cs_vSend); | ||
| 794 | if (LogAcceptsCategory("net")) printf("(aborted)\n"); | ||
| 795 | } | ||
| 796 | |||
| 797 | void EndMessage() | ||
| 798 | { | ||
| 799 | extern int nDropMessagesTest; | ||
| 800 | if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) | ||
| 801 | { | ||
| 802 | if (LogAcceptsCategory("net")) printf("dropmessages DROPPING SEND MESSAGE\n"); | ||
| 803 | AbortMessage(); | ||
| 804 | return; | ||
| 805 | } | ||
| 806 | |||
| 807 | if (nPushPos == -1) | ||
| 808 | return; | ||
| 809 | |||
| 810 | // Patch in the size | ||
| 811 | unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader); | ||
| 812 | memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); | ||
| 813 | |||
| 814 | if (LogAcceptsCategory("net")) printf("(%d bytes) ", nSize); | ||
| 815 | //for (int i = nPushPos+sizeof(CMessageHeader); i < min(vSend.size(), nPushPos+sizeof(CMessageHeader)+20U); i++) | ||
| 816 | // printf("%02x ", vSend[i] & 0xff); | ||
| 817 | if (LogAcceptsCategory("net")) printf("\n"); | ||
| 818 | |||
| 819 | nPushPos = -1; | ||
| 820 | LeaveCriticalSection(&cs_vSend); | ||
| 821 | } | ||
| 822 | |||
| 823 | void EndMessageAbortIfEmpty() | ||
| 824 | { | ||
| 825 | if (nPushPos == -1) | ||
| 826 | return; | ||
| 827 | int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader); | ||
| 828 | if (nSize > 0) | ||
| 829 | EndMessage(); | ||
| 830 | else | ||
| 831 | AbortMessage(); | ||
| 832 | } | ||
| 833 | |||
| 834 | const char* GetMessageCommand() const | ||
| 835 | { | ||
| 836 | if (nPushPos == -1) | ||
| 837 | return ""; | ||
| 838 | return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand); | ||
| 839 | } | ||
| 840 | |||
| 841 | |||
| 842 | |||
| 843 | |||
| 844 | // Ask this peer to fill in blocks we are missing, but only once per | ||
| 845 | // question. | ||
| 846 | // | ||
| 847 | // Every orphan block fires one of these, and an inv for a block we already | ||
| 848 | // hold as an orphan fires another. A node that is missing part of the | ||
| 849 | // chain therefore asks the same peer the same thing repeatedly: measured | ||
| 850 | // on a production node, 1287 orphan blocks out of 4117 processed, and 97 | ||
| 851 | // "already have block" from the answers coming back more than once. The | ||
| 852 | // duplicate answers create more orphans, which fire more requests. | ||
| 853 | // | ||
| 854 | // Remembering the last question asked breaks that loop. Same fix Bitcoin | ||
| 855 | // made in e2c2648c1. | ||
| 856 | // Body lives in net.cpp: it builds a CBlockLocator, which main.h defines, | ||
| 857 | // and main.h is included after this file. | ||
| 858 | void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); | ||
| 859 | |||
| 860 | void PushMessage(const char* pszCommand) | ||
| 861 | { | ||
| 862 | try | ||
| 863 | { | ||
| 864 | BeginMessage(pszCommand); | ||
| 865 | EndMessage(); | ||
| 866 | } | ||
| 867 | catch (...) | ||
| 868 | { | ||
| 869 | AbortMessage(); | ||
| 870 | throw; | ||
| 871 | } | ||
| 872 | } | ||
| 873 | |||
| 874 | template<typename T1> | ||
| 875 | void PushMessage(const char* pszCommand, const T1& a1) | ||
| 876 | { | ||
| 877 | try | ||
| 878 | { | ||
| 879 | BeginMessage(pszCommand); | ||
| 880 | vSend << a1; | ||
| 881 | EndMessage(); | ||
| 882 | } | ||
| 883 | catch (...) | ||
| 884 | { | ||
| 885 | AbortMessage(); | ||
| 886 | throw; | ||
| 887 | } | ||
| 888 | } | ||
| 889 | |||
| 890 | template<typename T1, typename T2> | ||
| 891 | void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) | ||
| 892 | { | ||
| 893 | try | ||
| 894 | { | ||
| 895 | BeginMessage(pszCommand); | ||
| 896 | vSend << a1 << a2; | ||
| 897 | EndMessage(); | ||
| 898 | } | ||
| 899 | catch (...) | ||
| 900 | { | ||
| 901 | AbortMessage(); | ||
| 902 | throw; | ||
| 903 | } | ||
| 904 | } | ||
| 905 | |||
| 906 | template<typename T1, typename T2, typename T3> | ||
| 907 | void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) | ||
| 908 | { | ||
| 909 | try | ||
| 910 | { | ||
| 911 | BeginMessage(pszCommand); | ||
| 912 | vSend << a1 << a2 << a3; | ||
| 913 | EndMessage(); | ||
| 914 | } | ||
| 915 | catch (...) | ||
| 916 | { | ||
| 917 | AbortMessage(); | ||
| 918 | throw; | ||
| 919 | } | ||
| 920 | } | ||
| 921 | |||
| 922 | template<typename T1, typename T2, typename T3, typename T4> | ||
| 923 | void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) | ||
| 924 | { | ||
| 925 | try | ||
| 926 | { | ||
| 927 | BeginMessage(pszCommand); | ||
| 928 | vSend << a1 << a2 << a3 << a4; | ||
| 929 | EndMessage(); | ||
| 930 | } | ||
| 931 | catch (...) | ||
| 932 | { | ||
| 933 | AbortMessage(); | ||
| 934 | throw; | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | template<typename T1, typename T2, typename T3, typename T4, typename T5> | ||
| 939 | void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) | ||
| 940 | { | ||
| 941 | try | ||
| 942 | { | ||
| 943 | BeginMessage(pszCommand); | ||
| 944 | vSend << a1 << a2 << a3 << a4 << a5; | ||
| 945 | EndMessage(); | ||
| 946 | } | ||
| 947 | catch (...) | ||
| 948 | { | ||
| 949 | AbortMessage(); | ||
| 950 | throw; | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | |||
| 955 | void PushRequest(const char* pszCommand, | ||
| 956 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 957 | { | ||
| 958 | uint256 hashReply; | ||
| 959 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 960 | |||
| 961 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 962 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 963 | |||
| 964 | PushMessage(pszCommand, hashReply); | ||
| 965 | } | ||
| 966 | |||
| 967 | template<typename T1> | ||
| 968 | void PushRequest(const char* pszCommand, const T1& a1, | ||
| 969 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 970 | { | ||
| 971 | uint256 hashReply; | ||
| 972 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 973 | |||
| 974 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 975 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 976 | |||
| 977 | PushMessage(pszCommand, hashReply, a1); | ||
| 978 | } | ||
| 979 | |||
| 980 | template<typename T1, typename T2> | ||
| 981 | void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, | ||
| 982 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 983 | { | ||
| 984 | uint256 hashReply; | ||
| 985 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 986 | |||
| 987 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 988 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 989 | |||
| 990 | PushMessage(pszCommand, hashReply, a1, a2); | ||
| 991 | } | ||
| 992 | |||
| 993 | |||
| 994 | |||
| 995 | bool IsSubscribed(unsigned int nChannel); | ||
| 996 | void Subscribe(unsigned int nChannel, unsigned int nHops=0); | ||
| 997 | void CancelSubscribe(unsigned int nChannel); | ||
| 998 | void Disconnect(); | ||
| 999 | }; | ||
| 1000 | |||
| 1001 | |||
| 1002 | |||
| 1003 | |||
| 1004 | |||
| 1005 | |||
| 1006 | |||
| 1007 | |||
| 1008 | |||
| 1009 | |||
| 1010 | inline void RelayInventory(const CInv& inv) | ||
| 1011 | { | ||
| 1012 | // Put on lists to offer to the other nodes | ||
| 1013 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1014 | foreach(CNode* pnode, vNodes) | ||
| 1015 | pnode->PushInventory(inv); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | template<typename T> | ||
| 1019 | void RelayMessage(const CInv& inv, const T& a) | ||
| 1020 | { | ||
| 1021 | CDataStream ss(SER_NETWORK); | ||
| 1022 | ss.reserve(10000); | ||
| 1023 | ss << a; | ||
| 1024 | RelayMessage(inv, ss); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | template<> | ||
| 1028 | inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) | ||
| 1029 | { | ||
| 1030 | CRITICAL_BLOCK(cs_mapRelay) | ||
| 1031 | { | ||
| 1032 | // Expire old relay messages | ||
| 1033 | while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) | ||
| 1034 | { | ||
| 1035 | mapRelay.erase(vRelayExpiration.front().second); | ||
| 1036 | vRelayExpiration.pop_front(); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | // Save original serialized message so newer versions are preserved | ||
| 1040 | mapRelay[inv] = ss; | ||
| 1041 | vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv)); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | RelayInventory(inv); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | |||
| 1048 | |||
| 1049 | |||
| 1050 | |||
| 1051 | |||
| 1052 | |||
| 1053 | |||
| 1054 | // | ||
| 1055 | // Templates for the publish and subscription system. | ||
| 1056 | // The object being published as T& obj needs to have: | ||
| 1057 | // a set<unsigned int> setSources member | ||
| 1058 | // specializations of AdvertInsert and AdvertErase | ||
| 1059 | // Currently implemented for CTable and CProduct. | ||
| 1060 | // | ||
| 1061 | |||
| 1062 | template<typename T> | ||
| 1063 | void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1064 | { | ||
| 1065 | // Add to sources | ||
| 1066 | obj.setSources.insert(pfrom->addr.ip); | ||
| 1067 | |||
| 1068 | if (!AdvertInsert(obj)) | ||
| 1069 | return; | ||
| 1070 | |||
| 1071 | // Relay | ||
| 1072 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1073 | foreach(CNode* pnode, vNodes) | ||
| 1074 | if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) | ||
| 1075 | pnode->PushMessage("publish", nChannel, nHops, obj); | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | template<typename T> | ||
| 1079 | void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1080 | { | ||
| 1081 | uint256 hash = obj.GetHash(); | ||
| 1082 | |||
| 1083 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1084 | foreach(CNode* pnode, vNodes) | ||
| 1085 | if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) | ||
| 1086 | pnode->PushMessage("pub-cancel", nChannel, nHops, hash); | ||
| 1087 | |||
| 1088 | AdvertErase(obj); | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | template<typename T> | ||
| 1092 | void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1093 | { | ||
| 1094 | // Remove a source | ||
| 1095 | obj.setSources.erase(pfrom->addr.ip); | ||
| 1096 | |||
| 1097 | // If no longer supported by any sources, cancel it | ||
| 1098 | if (obj.setSources.empty()) | ||
| 1099 | AdvertStopPublish(pfrom, nChannel, nHops, obj); | ||
| 1100 | } | ||