Merge pull request #2417 from patricklodder/1.14-serialize-getheaders

Reduce getheaders spam by serializing getheader requests per peer
This commit is contained in:
Patrick Lodder 2021-08-09 20:54:33 +02:00 committed by GitHub
commit 3a5a31c113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 6 deletions

View file

@ -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;

View file

@ -681,6 +681,9 @@ public:
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
// Counts getheaders requests sent to this peer
std::atomic<int64_t> nPendingHeaderRequests;
// Alert relay
std::vector<CAlert> vAlertToSend;

View file

@ -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<CBlockHeader> 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<bool>& 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);
}
}