Add header spam protection

This commit is contained in:
lateminer
2019-04-01 23:39:03 +03:00
parent 5d85509142
commit 248fb13834
3 changed files with 144 additions and 2 deletions

View File

@@ -474,6 +474,9 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE));
strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT));
}
strUsage += HelpMessageOpt("-headerspamfilter=<n>", strprintf(_("Use header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER));
strUsage += HelpMessageOpt("-headerspamfiltermaxsize=<n>", strprintf(_("Maximum size of the list of indexes in the header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE));
strUsage += HelpMessageOpt("-headerspamfiltermaxavg=<n>", strprintf(_("Maximum average size of an index occurrence in the header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER_MAX_AVG));
#ifdef ENABLE_WALLET
strUsage += HelpMessageGroup(_("Staking options:"));

View File

@@ -3,6 +3,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Header spam protection by Qtum
// Copyright (c) 2016-2019 The Qtum developers
// Copyright (c) 2017-2019 The Navcoin developers
#include "main.h"
#include "addrman.h"
@@ -267,6 +271,89 @@ struct CBlockReject {
uint256 hashBlock;
};
class CNodeHeaders
{
public:
CNodeHeaders():
maxSize(0),
maxAvg(0)
{
maxSize = GetArg("-headerspamfiltermaxsize", DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE);
maxAvg = GetArg("-headerspamfiltermaxavg", DEFAULT_HEADER_SPAM_FILTER_MAX_AVG);
}
bool addHeaders(int nBegin, int nEnd)
{
if(nBegin > 0 && nEnd > 0 && maxSize && maxAvg)
{
for(int point = nBegin; point<= nEnd; point++)
{
addPoint(point);
}
return true;
}
return false;
}
bool updateState(CValidationState& state, bool ret)
{
// No headers
size_t size = points.size();
if(size == 0)
return ret;
// Compute the number of the received headers
size_t nHeaders = 0;
for(auto point : points)
{
nHeaders += point.second;
}
// Compute the average value per height
double nAvgValue = (double)nHeaders / size;
// Ban the node if try to spam
bool banNode = (nAvgValue >= 1.5 * maxAvg && size >= maxAvg) ||
(nAvgValue >= maxAvg && nHeaders >= maxSize) ||
(nHeaders >= maxSize * 3);
if(banNode)
{
// Clear the points and ban the node
points.clear();
return state.DoS(100, false, REJECT_INVALID, "header-spam", false, "ban node for sending spam");
}
return ret;
}
private:
void addPoint(int height)
{
// Erace the last element in the list
if(points.size() == maxSize)
{
points.erase(points.begin());
}
// Add the point to the list
int occurrence = 0;
auto mi = points.find(height);
if (mi != points.end())
occurrence = (*mi).second;
occurrence++;
points[height] = occurrence;
}
private:
std::map<int,int> points;
size_t maxSize;
size_t maxAvg;
};
/**
* Maintain validation-specific state about nodes, protected by cs_main, instead
* by CNode's own locks. This simplifies asynchronous operation, where
@@ -316,6 +403,7 @@ struct CNodeState {
//! Whether this peer will send us cmpctblocks if we request them
bool fProvidesHeaderAndIDs;
*/
CNodeHeaders headers;
CNodeState() {
fCurrentlyConnected = false;
@@ -6009,12 +6097,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
return true;
}
bool ret = true;
bool bFirst = true;
string strError = "";
int nFirst = 0;
int nLast = 0;
CBlockIndex *pindexLast = NULL;
BOOST_FOREACH(const CBlockHeader& header, headers) {
CValidationState state;
if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) {
Misbehaving(pfrom->GetId(), 20);
return error("non-continuous headers sequence");
ret = false;
strError = "non-continuous headers sequence";
break;
}
// ToDo: enable header check for PoW blocks
if (!AcceptBlockHeader(header, state, chainparams, &pindexLast)) {
@@ -6022,11 +6120,42 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (state.IsInvalid(nDoS)) {
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
return error("invalid header received");
ret = false;
strError = "invalid header received";
break;
}
}
if (pindexLast) {
nLast = pindexLast->nHeight;
if (bFirst){
nFirst = pindexLast->nHeight;
bFirst = false;
}
}
}
// Do not activate spam filter during IBD
if (GetBoolArg("-headerspamfilter", DEFAULT_HEADER_SPAM_FILTER) && !IsInitialBlockDownload())
{
LOCK(cs_main);
CValidationState state;
CNodeState *nodestate = State(pfrom->GetId());
nodestate->headers.addHeaders(nFirst, nLast);
int nDoS;
ret = nodestate->headers.updateState(state, ret);
if (state.IsInvalid(nDoS)) {
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
ret = false;
strError = strError!="" ? strError + " / ": "";
strError = "header spam protection";
}
}
if (!ret)
return error(strError.c_str());
if (nodestate->nUnconnectingHeaders > 0) {
LogPrint("net", "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->id, nodestate->nUnconnectingHeaders);
}

View File

@@ -3,6 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Header spam protection by Qtum
// Copyright (c) 2016-2019 The Qtum developers
#ifndef BITCOIN_MAIN_H
#define BITCOIN_MAIN_H
@@ -139,6 +142,13 @@ static const bool DEFAULT_TESTSAFEMODE = false;
/** Default for using fee filter */
static const bool DEFAULT_FEEFILTER = true;
/** Default for -headerspamfilter, use header spam filter */
static const bool DEFAULT_HEADER_SPAM_FILTER = true;
/** Default for -headerspamfiltermaxsize, maximum size of the list of indexes in the header spam filter */
static const unsigned int DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE = 500;
/** Default for -headerspamfiltermaxavg, maximum average size of an index occurrence in the header spam filter */
static const unsigned int DEFAULT_HEADER_SPAM_FILTER_MAX_AVG = 10;
/** Maximum number of headers to announce when relaying blocks with headers message.*/
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;