Skip to content
Permalink
Browse files

Merge pull request #5947

36cba8f Alert if it is very likely we are getting a bad chain (Gavin Andresen)
  • Loading branch information...
laanwj committed May 26, 2015
2 parents e1412d3 + 36cba8f commit e9af4e65b5afaa870f6cdcf2f7c2ee56c703bffa
Showing with 133 additions and 0 deletions.
  1. +8 −0 src/init.cpp
  2. +59 −0 src/main.cpp
  3. +2 −0 src/main.h
  4. +64 −0 src/test/alert_tests.cpp
@@ -39,7 +39,9 @@

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/function.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
@@ -1392,6 +1394,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)

StartNode(threadGroup, scheduler);

// Monitor the chain, and alert if we get blocks much quicker or slower than expected
int64_t nPowTargetSpacing = Params().GetConsensus().nPowTargetSpacing;
CScheduler::Function f = boost::bind(&PartitionCheck, &IsInitialBlockDownload,
boost::ref(cs_main), boost::cref(chainActive), nPowTargetSpacing);
scheduler.scheduleEvery(f, nPowTargetSpacing);

#ifdef ENABLE_WALLET
// Generate coins in the background
if (pwalletMain)
@@ -28,6 +28,7 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/math/distributions/poisson.hpp>
#include <boost/thread.hpp>

using namespace std;
@@ -1685,6 +1686,64 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}

//
// Called periodically asynchronously; alerts if it smells like
// we're being fed a bad chain (blocks being generated much
// too slowly or too quickly).
//
void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing)
{
if (initialDownloadCheck()) return;

static int64_t lastAlertTime = 0;
int64_t now = GetAdjustedTime();
if (lastAlertTime > now-60*60*24) return; // Alert at most once per day

const int SPAN_HOURS=4;
const int SPAN_SECONDS=SPAN_HOURS*60*60;
int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing;

boost::math::poisson_distribution<double> poisson(BLOCKS_EXPECTED);

std::string strWarning;
int64_t startTime = GetAdjustedTime()-SPAN_SECONDS;

LOCK(cs);
int h = chain.Height();
while (h > 0 && chain[h]->GetBlockTime() >= startTime)
--h;
int nBlocks = chain.Height()-h;

// How likely is it to find that many by chance?
double p = boost::math::pdf(poisson, nBlocks);

LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS);
LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p);

// Aim for one false-positive about every fifty years of normal running:
const int FIFTY_YEARS = 50*365*24*60*60;
double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS);

if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED)
{
// Many fewer blocks than expected: alert!
strWarning = strprintf(_("WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)"),
nBlocks, SPAN_HOURS, BLOCKS_EXPECTED);
}
else if (p <= alertThreshold && nBlocks > BLOCKS_EXPECTED)
{
// Many more blocks than expected: alert!
strWarning = strprintf(_("WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)"),
nBlocks, SPAN_HOURS, BLOCKS_EXPECTED);
}
if (!strWarning.empty())
{
strMiscWarning = strWarning;
CAlert::Notify(strWarning, true);
lastAlertTime = now;
}
}

static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
@@ -194,6 +194,8 @@ bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, bool fSendTrickle);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Try to detect Partition (network isolation) attacks against us */
void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing);
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Format a string that describes several potential problems detected by the core */
@@ -7,10 +7,13 @@
//

#include "alert.h"
#include "chain.h"
#include "chainparams.h"
#include "clientversion.h"
#include "data/alertTests.raw.h"

#include "chainparams.h"
#include "main.h"
#include "serialize.h"
#include "streams.h"
#include "util.h"
@@ -193,4 +196,65 @@ BOOST_AUTO_TEST_CASE(AlertNotify)
SetMockTime(0);
}

static bool falseFunc() { return false; }

BOOST_AUTO_TEST_CASE(PartitionAlert)
{
// Test PartitionCheck
CCriticalSection csDummy;
CChain chainDummy;
CBlockIndex indexDummy[100];
CChainParams& params = Params(CBaseChainParams::MAIN);
int64_t nPowTargetSpacing = params.GetConsensus().nPowTargetSpacing;

// Generate fake blockchain timestamps relative to
// an arbitrary time:
int64_t now = 1427379054;
SetMockTime(now);
for (int i = 0; i < 100; i++)
{
indexDummy[i].phashBlock = NULL;
if (i == 0) indexDummy[i].pprev = NULL;
else indexDummy[i].pprev = &indexDummy[i-1];
indexDummy[i].nHeight = i;
indexDummy[i].nTime = now - (100-i)*nPowTargetSpacing;
// Other members don't matter, the partition check code doesn't
// use them
}
chainDummy.SetTip(&indexDummy[99]);

// Test 1: chain with blocks every nPowTargetSpacing seconds,
// as normal, no worries:
PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing);
BOOST_CHECK(strMiscWarning.empty());

// Test 2: go 3.5 hours without a block, expect a warning:
now += 3*60*60+30*60;
SetMockTime(now);
PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing);
BOOST_CHECK(!strMiscWarning.empty());
BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning);
strMiscWarning = "";

// Test 3: test the "partition alerts only go off once per day"
// code:
now += 60*10;
SetMockTime(now);
PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing);
BOOST_CHECK(strMiscWarning.empty());

// Test 4: get 2.5 times as many blocks as expected:
now += 60*60*24; // Pretend it is a day later
SetMockTime(now);
int64_t quickSpacing = nPowTargetSpacing*2/5;
for (int i = 0; i < 100; i++) // Tweak chain timestamps:
indexDummy[i].nTime = now - (100-i)*quickSpacing;
PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing);
BOOST_CHECK(!strMiscWarning.empty());
BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning);
strMiscWarning = "";

SetMockTime(0);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit e9af4e6

Please sign in to comment.
You can’t perform that action at this time.