diff --git a/src/net.cpp b/src/net.cpp index e89f1b11d..2117c59fd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2704,6 +2704,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn fPauseRecv = false; fPauseSend = false; nProcessQueueSize = 0; + nPendingHeaderRequests = 0; BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; diff --git a/src/net.h b/src/net.h index 868be7fd9..f5841a314 100644 --- a/src/net.h +++ b/src/net.h @@ -681,6 +681,9 @@ public: CAmount lastSentFeeFilter; int64_t nextSendTimeFeeFilter; + // Counts getheaders requests sent to this peer + std::atomic nPendingHeaderRequests; + // Alert relay std::vector vAlertToSend; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6de152ae8..dee17a733 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -731,6 +731,25 @@ void Misbehaving(NodeId pnode, int howmuch) } +// Dogecoin - 1.14 specific fix: do not request headers from a peer we are +// already requesting headers from, unless forced. +void RequestHeadersFrom(CNode* pto, CConnman& connman, const CBlockIndex* pindex, uint256 untilHash, bool fforceQuery) +{ + if (pto->nPendingHeaderRequests > 0) { + if (fforceQuery) { + LogPrint("net", "forcing getheaders request (%d) to peer=%d (%d open)\n", + pindex->nHeight, pto->id, pto->nPendingHeaderRequests); + } else { + LogPrint("net", "dropped getheaders request (%d) to peer=%d\n", pindex->nHeight, pto->id); + return; + } + } + + const CNetMsgMaker msgMaker(pto->GetSendVersion()); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindex), untilHash)); + pto->nPendingHeaderRequests += 1; + +} @@ -1560,7 +1579,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // fell back to inv we probably have a reorg which we should get the headers for first, // we now only provide a getheaders response here. When we receive the headers, we will // then ask for the blocks we need. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); + // Dogecoin: We force this check, in case we're only connected to nodes that send invs + RequestHeadersFrom(pfrom, connman, pindexBestHeader, inv.hash, true); LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } } @@ -1968,7 +1988,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + RequestHeadersFrom(pfrom, connman, pindexBestHeader, uint256(), true); return true; } } @@ -2225,6 +2245,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { std::vector headers; + if (pfrom->nPendingHeaderRequests > 0) + pfrom->nPendingHeaderRequests -= 1; + // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { @@ -2258,7 +2281,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // nUnconnectingHeaders gets reset back to 0. if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + // Dogecoin: allow a single getheaders query before triggering DoS + RequestHeadersFrom(pfrom, connman, pindexBestHeader, uint256(), true); LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -2312,8 +2336,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. + // + // Dogecoin: do not allow multiple getheader queries in parallel at + // this point - makes sure that any parallel queries will end here, + // preventing "getheaders" spam. LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); + RequestHeadersFrom(pfrom, connman, pindexLast, uint256(), false); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus(0)); @@ -2539,7 +2567,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Relay pfrom->setKnown.insert(alertHash); { - + connman.ForEachNode([&alert](CNode* pnode) { pnode->PushAlert(alert); @@ -2912,10 +2940,13 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr the peer's known best block. This wouldn't be possible if we requested starting at pindexBestHeader and got back an empty response. */ + // Dogecoin: make sure that if we are already processing an inv + // or header message from this peer caused by a new block being + // mined at chaintip, we do not send another getheaders request if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); + RequestHeadersFrom(pto, connman, pindexStart, uint256(), false); } }