Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anti Header Spam v2 #656

Merged
merged 9 commits into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 48 additions & 56 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,83 +281,52 @@ class CNodeHeaders
{
public:
CNodeHeaders():
maxSize(0),
maxAvg(0)
maxSize(0)
{
maxSize = GetArg("-headerspamfiltermaxsize", DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE);
maxAvg = GetArg("-headerspamfiltermaxavg", DEFAULT_HEADER_SPAM_FILTER_MAX_AVG);
maxSize = GetArg("-headerspamfiltermaxsize", MAX_HEADERS_RESULTS*2);
}

bool addHeaders(int nBegin, int nEnd)
void addHeaders(std::vector<uint256> hashes)
{

if(nBegin > 0 && nEnd > 0 && maxSize && maxAvg)
{

for(int point = nBegin; point<= nEnd; point++)
{
addPoint(point);
}

return true;
}

return false;
for (auto& it: hashes)
points.insert(it);
}

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)
int64_t nTime1 = GetTimeMicros();

for (auto it = points.begin(); it != points.end();)
{
nHeaders += point.second;
if (mapBlockIndex.count(*it) && (mapBlockIndex[*it]->nStatus & BLOCK_VALID_SCRIPTS) == BLOCK_VALID_SCRIPTS)
{
it = points.erase(it);
}
else
{
++it;
}
}

// Compute the average value per height
double nAvgValue = (double)nHeaders / size;
LogPrint("headerspam", "%s: Current size: %d Max size: %d\n",
__func__, points.size(), maxSize);

// Ban the node if try to spam
bool banNode = (nAvgValue >= 1.5 * maxAvg && size >= maxAvg) ||
(nAvgValue >= maxAvg && nHeaders >= maxSize) ||
(nHeaders >= maxSize * 3);
if(banNode)
if(points.size() > maxSize)
{
// Clear the points and ban the node
points.clear();
return state.DoS(100, false, REJECT_INVALID, "header-spam", false, "ban node for sending spam");
}

int64_t nTime2 = GetTimeMicros();
LogPrint("bench", "%s: Anti header spam took: %.2fms\n", __func__, 0.001 * (nTime2 - nTime1));

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;
std::set<uint256> points;
size_t maxSize;
size_t maxAvg;
};

/**
Expand Down Expand Up @@ -7309,6 +7278,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,

CBlockIndex *pindexLast = nullptr;

std::vector<uint256> vHeaderHashes;

for(const CBlock& header: headers) {
CValidationState state;
if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) {
Expand All @@ -7328,6 +7299,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
break;
}
}
vHeaderHashes.push_back(pblockheader.GetHash());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there ever a situation where this loop never runs and the vector is never populated? what effect would that have?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the hash is only added if AcceptBlockHeader returns true, which means the header has been inserted in mapBlockIndex

if (pindexLast) {
nLast = pindexLast->nHeight;
if (bFirst){
Expand All @@ -7343,7 +7315,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CValidationState state;
CNodeState *nodestate = State(pfrom->GetId());
CNodeHeaders& headers = ServiceHeaders(nodestate->address);
headers.addHeaders(nFirst, nLast);
headers.addHeaders(vHeaderHashes);
int nDoS;
ret = headers.updateState(state, ret);
if (state.IsInvalid(nDoS)) {
Expand Down Expand Up @@ -7443,6 +7415,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,

LogPrint("net", "received block %s peer=%d\n%s\n", block.GetHash().ToString(), pfrom->id, block.ToString());

bool ret = true;

if(GetBoolArg("-headerspamfilter", DEFAULT_HEADER_SPAM_FILTER) && !IsInitialBlockDownload())
{
LOCK(cs_main);
CValidationState state;
CNodeState *nodestate = State(pfrom->GetId());
CNodeHeaders& headers = ServiceHeaders(nodestate->address);
CBlockHeader pblockheader = CBlockHeader(block);
headers.addHeaders({pblockheader.GetHash()});
int nDoS;
ret = headers.updateState(state, ret);
if (state.IsInvalid(nDoS))
{
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);

return error("header spam protection");
}
}

CValidationState state;
// Process all blocks from whitelisted peers, even if not requested,
// unless we're still syncing with the network.
Expand All @@ -7460,7 +7453,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
Misbehaving(pfrom->GetId(), nDoS);
}
}

}


Expand Down
2 changes: 1 addition & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static const int MAX_SCRIPTCHECK_THREADS = 16;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 64;
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
Expand Down