diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 284 | ||||
| -rw-r--r-- | src/net.h | 45 |
2 files changed, 327 insertions, 2 deletions
diff --git a/src/main.cpp b/src/main.cpp index dded83a..681673c 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -65,6 +65,137 @@ CBlockIndex* pindexBest = NULL; | |||
| 65 | map<uint256, CBlock*> mapOrphanBlocks; | 65 | map<uint256, CBlock*> mapOrphanBlocks; |
| 66 | multimap<uint256, CBlock*> mapOrphanBlocksByPrev; | 66 | multimap<uint256, CBlock*> mapOrphanBlocksByPrev; |
| 67 | 67 | ||
| 68 | // Block bodies we have sent a getdata for and not yet received, so the | ||
| 69 | // failover pass in SendMessages can tell "requested and being served" from | ||
| 70 | // "requested and gone quiet" and re-issue the request to a different peer | ||
| 71 | // (#33). Keyed by block hash; setAsked is the peers already tried since the | ||
| 72 | // round last restarted, so the retry rotates instead of hammering the | ||
| 73 | // announcer that is not serving. Guarded by cs_main, which both the message | ||
| 74 | // handlers and SendMessages already hold. | ||
| 75 | // | ||
| 76 | // Capped small on purpose. At the tip there is one block in flight; during | ||
| 77 | // initial download there are legitimately hundreds outstanding to one peer, | ||
| 78 | // and rotating those would duplicate the whole download to every other peer. | ||
| 79 | // | ||
| 80 | // The cap is a shared resource and nothing on the path from inv to insertion | ||
| 81 | // can tell a real block hash from 32 random bytes -- AlreadyHave only asks | ||
| 82 | // whether we already hold the block, and there is no header to check work | ||
| 83 | // against. So the table does not refuse a new hash when it is full; it makes | ||
| 84 | // room. See MakeRoomInBlockFetch for the rule, which is what stops one peer | ||
| 85 | // filling all eight slots and switching failover off for everybody. | ||
| 86 | // | ||
| 87 | // The initialisers matter: the insertion path uses operator[] and then tests | ||
| 88 | // nTimeFirstAsked == 0 to detect a fresh entry. That worked on | ||
| 89 | // value-initialisation alone, but only for as long as nobody gave this struct | ||
| 90 | // a constructor. | ||
| 91 | struct CBlockFetch | ||
| 92 | { | ||
| 93 | int64 nTimeFirstAsked = 0; // when the first getdata for this block went out | ||
| 94 | int64 nTimeAsked = 0; // when the latest getdata went out | ||
| 95 | CAddress addrAnnounced; // peer whose announcement put this hash here | ||
| 96 | set<CAddress> setAsked; // peers tried since the round last restarted | ||
| 97 | }; | ||
| 98 | map<uint256, CBlockFetch> mapBlockFetch; | ||
| 99 | static const int BLOCK_FETCH_TRACK_MAX = 8; | ||
| 100 | // A block nobody has served for this long is not a fetch problem: the hash may | ||
| 101 | // have been announced and never existed. Drop the entry rather than getdata | ||
| 102 | // forever; a re-announcement recreates it. | ||
| 103 | static const int BLOCK_FETCH_GIVEUP_SECS = 10 * 60; | ||
| 104 | |||
| 105 | // Failover firing at all is worth saying out loud: it means peers are | ||
| 106 | // announcing blocks they then do not serve, which is the pathology of #33 and | ||
| 107 | // the thing nobody could otherwise see. Naming the peer on every re-request is | ||
| 108 | // a different matter. A peer can drive that line as often as it likes by | ||
| 109 | // announcing hashes it will not serve, and what it prints is a peer address, | ||
| 110 | // which is the one thing the .btf transport otherwise takes trouble not to | ||
| 111 | // spread around. So the per-peer detail sits behind the net category and the | ||
| 112 | // unconditional line is a count over a window no peer can shorten: the first | ||
| 113 | // failover after a quiet spell prints at once, because the change from "blocks | ||
| 114 | // arrive" to "blocks are announced and not served" is the event worth seeing, | ||
| 115 | // and everything after it is summarised once a minute. | ||
| 116 | static const int BLOCK_FETCH_REPORT_SECS = 60; | ||
| 117 | static int64 nBlockFetchWindowStart = 0; | ||
| 118 | static int nBlockFetchFailovers = 0; | ||
| 119 | |||
| 120 | // How stale our own best block may be before we stop calling this the tip. | ||
| 121 | // Blocks target two minutes, so this is thirty intervals: far longer than any | ||
| 122 | // stall worth failing over from -- the one in #33 ran sixteen minutes -- and | ||
| 123 | // far shorter than catching up, which starts hours or days back. | ||
| 124 | static const int BLOCK_FETCH_TIP_AGE_SECS = 60 * 60; | ||
| 125 | |||
| 126 | // Whether this node is tracking the tip rather than still catching up. | ||
| 127 | // | ||
| 128 | // Measured off the age of our own best block, not off any height a peer told | ||
| 129 | // us. nStartingHeight is optional in the version message and every node | ||
| 130 | // released so far sends the short form, so it is -1 for essentially every | ||
| 131 | // peer; and where it is filled in later it is filled in from the height of a | ||
| 132 | // block that peer served us, which during catch-up tracks our own height and | ||
| 133 | // would report "at the tip" for the whole download. Block timestamps are the | ||
| 134 | // one clock here that peers cannot move: CheckBlock refuses anything more than | ||
| 135 | // two hours ahead and AcceptBlock refuses anything at or before the previous | ||
| 136 | // median, so the only way to make this read "at the tip" falsely is to mine, | ||
| 137 | // and all that buys is the 15s spacing this code had unconditionally anyway. | ||
| 138 | bool IsBlockFetchNearTip() | ||
| 139 | { | ||
| 140 | return pindexBest && (int64)pindexBest->nTime > GetTime() - BLOCK_FETCH_TIP_AGE_SECS; | ||
| 141 | } | ||
| 142 | |||
| 143 | // Drop one entry so a hash announced by addrNew can be tracked. | ||
| 144 | // | ||
| 145 | // Refusing the insertion instead was a remote off-switch for the whole | ||
| 146 | // feature. An inv carrying eight hashes that never existed filled every slot | ||
| 147 | // -- nothing between inv and here can tell those from real blocks -- and no | ||
| 148 | // genuinely new block was tracked again until the give-up timer expired ten | ||
| 149 | // minutes later, re-armable for one message per ten minutes from a single | ||
| 150 | // connection. | ||
| 151 | // | ||
| 152 | // So evict instead, by occupancy: a peer may only take a slot from a peer | ||
| 153 | // holding strictly more slots than itself, and otherwise recycles one of its | ||
| 154 | // own. That is what keeps eviction from becoming the same attack in a new | ||
| 155 | // shape. A peer that has filled the table is by definition the heaviest | ||
| 156 | // holder, so its own entries are the only ones it can displace, while a peer | ||
| 157 | // announcing its first block holds nothing and therefore always evicts from | ||
| 158 | // the heaviest -- a genuine announcement is never refused a slot, whatever the | ||
| 159 | // attacker holds or however many connections it holds it from. The one case | ||
| 160 | // that costs a real fetch is eight distinct peers holding one entry each, | ||
| 161 | // where a ninth takes the oldest; that is a busy healthy node, not an attack, | ||
| 162 | // and losing the oldest of eight beats refusing the ninth. | ||
| 163 | static void MakeRoomInBlockFetch(const CAddress& addrNew) | ||
| 164 | { | ||
| 165 | if (mapBlockFetch.empty()) | ||
| 166 | return; | ||
| 167 | |||
| 168 | map<CAddress, int> mapHeld; | ||
| 169 | for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end(); ++mi) | ||
| 170 | mapHeld[(*mi).second.addrAnnounced]++; | ||
| 171 | |||
| 172 | int nMine = mapHeld.count(addrNew) ? mapHeld[addrNew] : 0; | ||
| 173 | |||
| 174 | int nMax = 0; | ||
| 175 | CAddress addrVictim; | ||
| 176 | for (map<CAddress, int>::iterator mi = mapHeld.begin(); mi != mapHeld.end(); ++mi) | ||
| 177 | if ((*mi).second > nMax) | ||
| 178 | { | ||
| 179 | nMax = (*mi).second; | ||
| 180 | addrVictim = (*mi).first; | ||
| 181 | } | ||
| 182 | |||
| 183 | // Nobody is holding more than this peer, so it recycles its own oldest | ||
| 184 | // rather than taking a slot off an equal. | ||
| 185 | if (nMax <= nMine) | ||
| 186 | addrVictim = addrNew; | ||
| 187 | |||
| 188 | map<uint256, CBlockFetch>::iterator miVictim = mapBlockFetch.end(); | ||
| 189 | for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end(); ++mi) | ||
| 190 | if ((*mi).second.addrAnnounced == addrVictim && | ||
| 191 | (miVictim == mapBlockFetch.end() || | ||
| 192 | (*mi).second.nTimeFirstAsked < (*miVictim).second.nTimeFirstAsked)) | ||
| 193 | miVictim = mi; | ||
| 194 | |||
| 195 | if (miVictim != mapBlockFetch.end()) | ||
| 196 | mapBlockFetch.erase(miVictim); | ||
| 197 | } | ||
| 198 | |||
| 68 | map<uint256, CDataStream*> mapOrphanTransactions; | 199 | map<uint256, CDataStream*> mapOrphanTransactions; |
| 69 | multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev; | 200 | multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev; |
| 70 | 201 | ||
| @@ -3292,10 +3423,56 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | |||
| 3292 | CInv inv(MSG_BLOCK, pblock->GetHash()); | 3423 | CInv inv(MSG_BLOCK, pblock->GetHash()); |
| 3293 | pfrom->AddInventoryKnown(inv); | 3424 | pfrom->AddInventoryKnown(inv); |
| 3294 | 3425 | ||
| 3426 | // GetHash() covers the 80-byte header and nothing else, so the hash | ||
| 3427 | // coming out right does not make this the block we asked for. Every | ||
| 3428 | // peer that saw X announced holds X's header, and can answer our | ||
| 3429 | // getdata with that header bolted onto a transaction list of its own | ||
| 3430 | // invention -- the reply still hashes to X. hashMerkleRoot is the only | ||
| 3431 | // thing binding the header to the body, so check it here, before the | ||
| 3432 | // mapBlockFetch entry is touched and before CheckBlock spends a | ||
| 3433 | // RandomX hash on a body already known to be forged. | ||
| 3434 | // | ||
| 3435 | // There is no honest way to arrive here. Peer traffic rides the .btf | ||
| 3436 | // channel, which is XChaCha20-Poly1305, so a body that arrives at all | ||
| 3437 | // arrived intact: a mismatch is the peer's own construction, not line | ||
| 3438 | // noise. Drop the peer and leave the fetch outstanding so the failover | ||
| 3439 | // sweep asks somebody else for the real X, rather than recording X as | ||
| 3440 | // delivered on the strength of a header anyone could copy. | ||
| 3441 | if (pblock->hashMerkleRoot != pblock->BuildMerkleTree()) | ||
| 3442 | { | ||
| 3443 | if (LogAcceptsCategory("net")) | ||
| 3444 | printf("peer %s served a forged body for %s\n", | ||
| 3445 | pfrom->addr.ToString().c_str(), inv.ToString().c_str()); | ||
| 3446 | // One line per connection: ProcessMessages keeps draining the | ||
| 3447 | // receive buffer after fDisconnect is set, so a peer that packs | ||
| 3448 | // many forged bodies into one buffer must not get one log line | ||
| 3449 | // for each of them. | ||
| 3450 | if (!pfrom->fDisconnect) | ||
| 3451 | printf("block fetch: a peer answered the request for %s with a body that is not " | ||
| 3452 | "that block -- dropping it\n", inv.hash.ToString().substr(0,14).c_str()); | ||
| 3453 | pfrom->fDisconnect = true; | ||
| 3454 | return error("ProcessMessage() : forged block body for %s", | ||
| 3455 | inv.hash.ToString().substr(0,14).c_str()); | ||
| 3456 | } | ||
| 3457 | |||
| 3295 | if (ProcessBlock(pfrom, pblock.release())) | 3458 | if (ProcessBlock(pfrom, pblock.release())) |
| 3296 | { | 3459 | { |
| 3297 | mapAlreadyAskedFor.erase(inv); | 3460 | mapAlreadyAskedFor.erase(inv); |
| 3298 | 3461 | ||
| 3462 | // The body arrived and validated, so the fetch really is over: | ||
| 3463 | // the block is now in mapBlockIndex, or parked in mapOrphanBlocks, | ||
| 3464 | // and either way AlreadyHave answers for it from here on. | ||
| 3465 | // | ||
| 3466 | // This has to stay inside the success branch, next to the erase | ||
| 3467 | // above. When validation rejects a body nothing is stored, | ||
| 3468 | // AlreadyHave stays false, and the mapAskFor entry was already | ||
| 3469 | // consumed when the getdata went out -- so erasing the tracking | ||
| 3470 | // here would leave the block requested from nobody at all until | ||
| 3471 | // some peer happened to re-announce it. That is exactly the stall | ||
| 3472 | // of #33, and erasing before ProcessBlock made it reachable on | ||
| 3473 | // demand. | ||
| 3474 | mapBlockFetch.erase(inv.hash); | ||
| 3475 | |||
| 3299 | // A peer that just handed us a block is demonstrably at least at | 3476 | // A peer that just handed us a block is demonstrably at least at |
| 3300 | // that block's height. Without this, nStartingHeight is a snapshot | 3477 | // that block's height. Without this, nStartingHeight is a snapshot |
| 3301 | // taken once during the version handshake and never touched again, | 3478 | // taken once during the version handshake and never touched again, |
| @@ -3420,12 +3597,119 @@ bool SendMessages(CNode* pto) | |||
| 3420 | const CInv& inv = (*pto->mapAskFor.begin()).second; | 3597 | const CInv& inv = (*pto->mapAskFor.begin()).second; |
| 3421 | if (LogAcceptsCategory("net")) printf("sending getdata: %s\n", inv.ToString().c_str()); | 3598 | if (LogAcceptsCategory("net")) printf("sending getdata: %s\n", inv.ToString().c_str()); |
| 3422 | if (!AlreadyHave(txdb, inv)) | 3599 | if (!AlreadyHave(txdb, inv)) |
| 3600 | { | ||
| 3423 | vAskFor.push_back(inv); | 3601 | vAskFor.push_back(inv); |
| 3602 | // Blocks get their delivery tracked so the failover pass | ||
| 3603 | // below can chase the ones that go quiet. | ||
| 3604 | if (inv.type == MSG_BLOCK) | ||
| 3605 | { | ||
| 3606 | // Full, and this is a hash we are not already tracking: | ||
| 3607 | // make room rather than drop it on the floor, or one peer | ||
| 3608 | // announcing eight hashes turns failover off for everyone. | ||
| 3609 | if (!mapBlockFetch.count(inv.hash) && | ||
| 3610 | mapBlockFetch.size() >= BLOCK_FETCH_TRACK_MAX) | ||
| 3611 | MakeRoomInBlockFetch(pto->addr); | ||
| 3612 | |||
| 3613 | if (mapBlockFetch.count(inv.hash) || | ||
| 3614 | mapBlockFetch.size() < BLOCK_FETCH_TRACK_MAX) | ||
| 3615 | { | ||
| 3616 | CBlockFetch& fetch = mapBlockFetch[inv.hash]; | ||
| 3617 | if (fetch.nTimeFirstAsked == 0) | ||
| 3618 | { | ||
| 3619 | fetch.nTimeFirstAsked = GetTime(); | ||
| 3620 | // Who is answerable for this hash existing. Set | ||
| 3621 | // once, on creation: the failover sweep asks other | ||
| 3622 | // peers later, and none of them chose to announce | ||
| 3623 | // it, so none of them should be charged for it. | ||
| 3624 | fetch.addrAnnounced = pto->addr; | ||
| 3625 | } | ||
| 3626 | fetch.nTimeAsked = GetTime(); | ||
| 3627 | fetch.setAsked.insert(pto->addr); | ||
| 3628 | } | ||
| 3629 | } | ||
| 3630 | } | ||
| 3424 | pto->mapAskFor.erase(pto->mapAskFor.begin()); | 3631 | pto->mapAskFor.erase(pto->mapAskFor.begin()); |
| 3425 | } | 3632 | } |
| 3426 | if (!vAskFor.empty()) | 3633 | if (!vAskFor.empty()) |
| 3427 | pto->PushMessage("getdata", vAskFor); | 3634 | pto->PushMessage("getdata", vAskFor); |
| 3428 | 3635 | ||
| 3636 | |||
| 3637 | // | ||
| 3638 | // Message: getdata (block fetch failover) | ||
| 3639 | // | ||
| 3640 | // A requested block that has not arrived in BLOCK_FETCH_RETRY_SECS is | ||
| 3641 | // not still in transit -- a body is at most 1 MB and somebody | ||
| 3642 | // demonstrably has it -- so ask somebody else instead of waiting on | ||
| 3643 | // whoever was asked first (#33). Each peer the message handler visits | ||
| 3644 | // takes at most one turn per block per round; once every connected | ||
| 3645 | // peer has had its turn and a further interval passes with nobody | ||
| 3646 | // new to ask, the round restarts. A peer that declared itself a | ||
| 3647 | // client cannot serve block bodies and is no use as a target. | ||
| 3648 | if (!pto->fClient) | ||
| 3649 | { | ||
| 3650 | vector<CInv> vFailover; | ||
| 3651 | int64 nNowSecs = GetTime(); | ||
| 3652 | for (map<uint256, CBlockFetch>::iterator mi = mapBlockFetch.begin(); mi != mapBlockFetch.end();) | ||
| 3653 | { | ||
| 3654 | CBlockFetch& fetch = (*mi).second; | ||
| 3655 | CInv inv(MSG_BLOCK, (*mi).first); | ||
| 3656 | if (AlreadyHave(txdb, inv)) | ||
| 3657 | { | ||
| 3658 | // Arrived by some other road -- our own miner, an orphan | ||
| 3659 | // parked in mapOrphanBlocks. Nothing left to chase. | ||
| 3660 | mapBlockFetch.erase(mi++); | ||
| 3661 | continue; | ||
| 3662 | } | ||
| 3663 | if (nNowSecs - fetch.nTimeFirstAsked > BLOCK_FETCH_GIVEUP_SECS) | ||
| 3664 | { | ||
| 3665 | printf("block fetch: giving up on %s -- no peer served it in %d s\n", | ||
| 3666 | inv.ToString().c_str(), (int)(nNowSecs - fetch.nTimeFirstAsked)); | ||
| 3667 | mapBlockFetch.erase(mi++); | ||
| 3668 | continue; | ||
| 3669 | } | ||
| 3670 | if (nNowSecs - fetch.nTimeAsked >= BLOCK_FETCH_RETRY_SECS) | ||
| 3671 | { | ||
| 3672 | // The handler loop visits every peer within ~100ms, so a | ||
| 3673 | // request stale by two full intervals means nobody outside | ||
| 3674 | // setAsked was there to ask. Start the round over. | ||
| 3675 | if (nNowSecs - fetch.nTimeAsked >= 2 * BLOCK_FETCH_RETRY_SECS) | ||
| 3676 | fetch.setAsked.clear(); | ||
| 3677 | if (!fetch.setAsked.count(pto->addr)) | ||
| 3678 | { | ||
| 3679 | // Which block, how long, and which peer we are trying | ||
| 3680 | // next. Behind the category because it names a peer | ||
| 3681 | // and a peer decides how often it is written. | ||
| 3682 | if (LogAcceptsCategory("net")) | ||
| 3683 | printf("block fetch failover: %s unanswered for %d s, re-requesting from %s\n", | ||
| 3684 | inv.ToString().c_str(), (int)(nNowSecs - fetch.nTimeAsked), | ||
| 3685 | pto->addr.ToString().c_str()); | ||
| 3686 | |||
| 3687 | // The unconditional half: no addresses, and at most | ||
| 3688 | // one line per BLOCK_FETCH_REPORT_SECS however hard a | ||
| 3689 | // peer tries. See the counters for the reasoning. | ||
| 3690 | nBlockFetchFailovers++; | ||
| 3691 | if (nNowSecs - nBlockFetchWindowStart >= BLOCK_FETCH_REPORT_SECS) | ||
| 3692 | { | ||
| 3693 | printf("block fetch: %d block request%s went unanswered and %s been " | ||
| 3694 | "re-asked of other peers (%d tracked)\n", | ||
| 3695 | nBlockFetchFailovers, nBlockFetchFailovers == 1 ? "" : "s", | ||
| 3696 | nBlockFetchFailovers == 1 ? "has" : "have", | ||
| 3697 | (int)mapBlockFetch.size()); | ||
| 3698 | nBlockFetchWindowStart = nNowSecs; | ||
| 3699 | nBlockFetchFailovers = 0; | ||
| 3700 | } | ||
| 3701 | |||
| 3702 | vFailover.push_back(inv); | ||
| 3703 | fetch.nTimeAsked = nNowSecs; | ||
| 3704 | fetch.setAsked.insert(pto->addr); | ||
| 3705 | } | ||
| 3706 | } | ||
| 3707 | ++mi; | ||
| 3708 | } | ||
| 3709 | if (!vFailover.empty()) | ||
| 3710 | pto->PushMessage("getdata", vFailover); | ||
| 3711 | } | ||
| 3712 | |||
| 3429 | } | 3713 | } |
| 3430 | return true; | 3714 | return true; |
| 3431 | } | 3715 | } |
| @@ -23,6 +23,12 @@ enum | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | // Defined in main.cpp. True while this node is tracking the tip rather than | ||
| 27 | // still catching up, measured off the age of our own best block -- see there | ||
| 28 | // for why that and not a peer-reported height. Declared here because net.h is | ||
| 29 | // parsed before main.h and CNode::AskFor needs it. | ||
| 30 | bool IsBlockFetchNearTip(); | ||
| 31 | |||
| 26 | bool GetMyExternalIP(unsigned int& ipRet); | 32 | bool GetMyExternalIP(unsigned int& ipRet); |
| 27 | CNode* ConnectNodeBtf(const string& strBtfAddr); | 33 | CNode* ConnectNodeBtf(const string& strBtfAddr); |
| 28 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, | 34 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, |
| @@ -89,6 +95,15 @@ static const int BTF_RECV_TIMEOUT_SECS = 30 * 60; | |||
| 89 | static const int BTF_SEND_STALL_SECS = 10 * 60; | 95 | static const int BTF_SEND_STALL_SECS = 10 * 60; |
| 90 | static const int BTF_HANDSHAKE_GRACE_SECS = 60; | 96 | static const int BTF_HANDSHAKE_GRACE_SECS = 60; |
| 91 | 97 | ||
| 98 | // How long a requested block gets before it is asked for again, in seconds. | ||
| 99 | // Used twice: CNode::AskFor spaces the queued per-announcer block retries this | ||
| 100 | // far apart instead of the two minutes transactions get, and the failover | ||
| 101 | // sweep in main.cpp re-issues a getdata to a different peer once a sent | ||
| 102 | // request has gone unanswered this long. A block body is at most 1 MB and | ||
| 103 | // somebody demonstrably has it -- they announced it -- so on this timescale | ||
| 104 | // "not here yet" means "not coming from that peer", not "still in transit". | ||
| 105 | static const int BLOCK_FETCH_RETRY_SECS = 15; | ||
| 106 | |||
| 92 | // A serialized object cannot be larger than MAX_SIZE, so no valid network | 107 | // 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 | 108 | // message needs the old 256 MB header allowance. Keep the wire cap aligned with |
| 94 | // what the deserializer can accept. How long a peer may sit on a partial | 109 | // what the deserializer can accept. How long a peer may sit on a partial |
| @@ -795,8 +810,34 @@ public: | |||
| 795 | static int64 nLastTime; | 810 | static int64 nLastTime; |
| 796 | nLastTime = nNow = max(nNow, ++nLastTime); | 811 | nLastTime = nNow = max(nNow, ++nLastTime); |
| 797 | 812 | ||
| 798 | // Each retry is 2 minutes after the last | 813 | // Each retry is 2 minutes after the last -- and because nRequestTime |
| 799 | nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow); | 814 | // is shared across peers, the same spacing is what strings requests |
| 815 | // for one inv across its announcers. For transactions that is pacing; | ||
| 816 | // for a new block it was the stall of issue #33: nine peers announce | ||
| 817 | // the block, the first one asked does not serve it, and the ninth | ||
| 818 | // candidate is not even asked until sixteen minutes in, with the pool | ||
| 819 | // mining on the stale tip the whole time. | ||
| 820 | // | ||
| 821 | // Blocks retry on the fetch timescale, but only while we are at the | ||
| 822 | // tip. That is where the stall costs something -- the pool keeps | ||
| 823 | // handing out jobs against a dead tip -- and it is also where the | ||
| 824 | // spacing is nearly free: one block is in flight, and its body has | ||
| 825 | // almost always arrived before the second announcer's turn comes | ||
| 826 | // round, so AlreadyHave drops the duplicate before a getdata is sent. | ||
| 827 | // | ||
| 828 | // While catching up neither half holds. A peer working through a | ||
| 829 | // 500-deep queue can be minutes away from block N without anything | ||
| 830 | // being wrong, so at 15s the second announcer is asked at +15s, the | ||
| 831 | // third at +30s, and the bulk download arrives once per announcer -- | ||
| 832 | // up to eight times over. Nothing caps that: the eight-entry cap is on | ||
| 833 | // mapBlockFetch, which is a different mechanism from this spacing. | ||
| 834 | // Two minutes is the right number there, and it is what this line did | ||
| 835 | // before. Failover at the tip does not depend on this being 15s in any | ||
| 836 | // case: mapBlockFetch re-issues a stalled getdata to the next peer on | ||
| 837 | // its own timer, which is what actually fixes #33. | ||
| 838 | bool fFast = (inv.type == MSG_BLOCK && IsBlockFetchNearTip()); | ||
| 839 | int64 nRetryUsecs = (fFast ? BLOCK_FETCH_RETRY_SECS : 2 * 60) * 1000000LL; | ||
| 840 | nRequestTime = max(nRequestTime + nRetryUsecs, nNow); | ||
| 800 | mapAskFor.insert(make_pair(nRequestTime, inv)); | 841 | mapAskFor.insert(make_pair(nRequestTime, inv)); |
| 801 | } | 842 | } |
| 802 | 843 | ||