Skip to content

Commit

Permalink
Merge upstream pull request xpc-wg#94
Browse files Browse the repository at this point in the history
  • Loading branch information
naomi-mcrn committed Jul 30, 2019
2 parents 1cbeff7 + 3d9cf20 commit 4cca090
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 7 deletions.
4 changes: 3 additions & 1 deletion src/init.cpp
Expand Up @@ -516,7 +516,9 @@ void SetupServerArgs()
gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC);
gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", false, OptionsCategory::RPC);

gArgs.AddArg("-headerspamfilter=<n>", strprintf(_("Use header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-headerspamfiltermaxsize=<n>", strprintf(_("Maximum size of the list of indexes in the header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-headerspamfiltermaxavg=<n>", strprintf(_("Maximum average size of an index occurrence in the header spam filter (default: %u)"), DEFAULT_HEADER_SPAM_FILTER_MAX_AVG), false, OptionsCategory::NODE_RELAY);
#if HAVE_DECL_DAEMON
gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS);
#else
Expand Down
112 changes: 108 additions & 4 deletions src/net_processing.cpp
Expand Up @@ -181,6 +181,93 @@ struct CBlockReject {
uint256 hashBlock;
};

class CNodeHeaders
{
public:
CNodeHeaders():
maxSize(0),
maxAvg(0)
{
maxSize = gArgs.GetArg("-headerspamfiltermaxsize", DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE);
maxAvg = gArgs.GetArg("-headerspamfiltermaxavg", DEFAULT_HEADER_SPAM_FILTER_MAX_AVG);
}

bool addHeaders(const CBlockIndex *pindexFirst, const CBlockIndex *pindexLast)
{
if(pindexFirst && pindexLast && maxSize && maxAvg)
{
// Get the begin block index
int nBegin = pindexFirst->nHeight;

// Get the end block index
int nEnd = pindexLast->nHeight;

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
Expand Down Expand Up @@ -270,6 +357,8 @@ struct CNodeState {

ChainSyncTimeoutState m_chain_sync;

CNodeHeaders headers;

//! Time of last new block announcement
int64_t m_last_block_announcement;

Expand Down Expand Up @@ -310,7 +399,22 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}

static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool ProcessNetBlockHeaders(CNode* pfrom, const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr)
{
const CBlockIndex* pindexFirst = nullptr;
bool ret = ProcessNewBlockHeaders(block, state, chainparams, ppindex, first_invalid, &pindexFirst);
if(gArgs.GetBoolArg("-headerspamfilter", DEFAULT_HEADER_SPAM_FILTER))
{
LOCK(cs_main);
CNodeState *nodestate = State(pfrom->GetId());
const CBlockIndex *pindexLast = ppindex == nullptr ? nullptr : *ppindex;
nodestate->headers.addHeaders(pindexFirst, pindexLast);
return nodestate->headers.updateState(state, ret);
}
return ret;
}

void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;

Expand Down Expand Up @@ -515,7 +619,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);

if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork <= chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
Expand Down Expand Up @@ -1409,7 +1513,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve

CValidationState state;
CBlockHeader first_invalid_header;
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
if (!ProcessNetBlockHeaders(pfrom, headers, state, chainparams, &pindexLast, &first_invalid_header)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
LOCK(cs_main);
Expand Down Expand Up @@ -2411,7 +2515,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr

const CBlockIndex *pindex = nullptr;
CValidationState state;
if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (!ProcessNetBlockHeaders(pfrom, {cmpctblock.header}, state, chainparams, &pindex)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
if (nDoS > 0) {
Expand Down
8 changes: 8 additions & 0 deletions src/net_processing.h
Expand Up @@ -9,6 +9,7 @@
#include <net.h>
#include <validationinterface.h>
#include <consensus/params.h>
#include <consensus/consensus.h>

/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
Expand All @@ -17,6 +18,13 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
/** Default for BIP61 (sending reject messages) */
static constexpr bool DEFAULT_ENABLE_BIP61 = 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 = COINBASE_MATURITY;
/** 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;

class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
Expand Down
8 changes: 7 additions & 1 deletion src/validation.cpp
Expand Up @@ -3693,11 +3693,12 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
}

// Exposed wrapper for AcceptBlockHeader
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid)
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid, const CBlockIndex** pindexFirst)
{
if (first_invalid != nullptr) first_invalid->SetNull();
{
LOCK(cs_main);
bool bFirst = true;
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
Expand All @@ -3706,6 +3707,11 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
}
if (ppindex) {
*ppindex = pindex;
if(bFirst && pindexFirst)
{
*pindexFirst = pindex;
bFirst = false;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/validation.h
Expand Up @@ -243,7 +243,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
* @param[out] first_invalid First header that fails validation, if one exists
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main);
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr, const CBlockIndex** pindexFirst = nullptr) LOCKS_EXCLUDED(cs_main);

/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
Expand Down

0 comments on commit 4cca090

Please sign in to comment.