Orphan block fill-up-memory attack prevention
This commit is contained in:
parent
eb5fff9e16
commit
10fd7f6689
5 changed files with 132 additions and 8 deletions
|
@ -2,16 +2,23 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "checkpoints.h"
|
|
||||||
#include "uint256.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
|
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
#include "headers.h"
|
||||||
|
#include "checkpoints.h"
|
||||||
|
|
||||||
namespace Checkpoints
|
namespace Checkpoints
|
||||||
{
|
{
|
||||||
typedef std::map<int, uint256> MapCheckpoints;
|
typedef std::map<int, uint256> MapCheckpoints;
|
||||||
|
|
||||||
|
//
|
||||||
|
// What makes a good checkpoint block?
|
||||||
|
// + Is surrounded by blocks with reasonable timestamps
|
||||||
|
// (no blocks before with a timestamp after, none after with
|
||||||
|
// timestamp before)
|
||||||
|
// + Contains no strange transactions
|
||||||
|
//
|
||||||
static MapCheckpoints mapCheckpoints =
|
static MapCheckpoints mapCheckpoints =
|
||||||
boost::assign::map_list_of
|
boost::assign::map_list_of
|
||||||
( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
|
( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
|
||||||
|
@ -36,8 +43,23 @@ namespace Checkpoints
|
||||||
|
|
||||||
int GetTotalBlocksEstimate()
|
int GetTotalBlocksEstimate()
|
||||||
{
|
{
|
||||||
if (fTestNet) return 0; // Testnet has no checkpoints
|
if (fTestNet) return 0;
|
||||||
|
|
||||||
return mapCheckpoints.rbegin()->first;
|
return mapCheckpoints.rbegin()->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
|
||||||
|
{
|
||||||
|
if (fTestNet) return NULL;
|
||||||
|
|
||||||
|
int64 nResult;
|
||||||
|
BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
|
||||||
|
{
|
||||||
|
const uint256& hash = i.second;
|
||||||
|
std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
|
||||||
|
if (t != mapBlockIndex.end())
|
||||||
|
return t->second;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
#ifndef BITCOIN_CHECKPOINT_H
|
#ifndef BITCOIN_CHECKPOINT_H
|
||||||
#define BITCOIN_CHECKPOINT_H
|
#define BITCOIN_CHECKPOINT_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
class uint256;
|
class uint256;
|
||||||
|
class CBlockIndex;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block-chain checkpoints are compiled-in sanity checks.
|
// Block-chain checkpoints are compiled-in sanity checks.
|
||||||
|
@ -17,6 +21,9 @@ namespace Checkpoints
|
||||||
|
|
||||||
// Return conservative estimate of total number of blocks, 0 if unknown
|
// Return conservative estimate of total number of blocks, 0 if unknown
|
||||||
int GetTotalBlocksEstimate();
|
int GetTotalBlocksEstimate();
|
||||||
|
|
||||||
|
// Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
|
||||||
|
CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
49
src/main.cpp
49
src/main.cpp
|
@ -659,11 +659,32 @@ int64 static GetBlockValue(int nHeight, int64 nFees)
|
||||||
return nSubsidy + nFees;
|
return nSubsidy + nFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
||||||
|
static const int64 nTargetSpacing = 10 * 60;
|
||||||
|
static const int64 nInterval = nTargetTimespan / nTargetSpacing;
|
||||||
|
|
||||||
|
//
|
||||||
|
// minimum amount of work that could possibly be required nTime after
|
||||||
|
// minimum work required was nBase
|
||||||
|
//
|
||||||
|
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
|
||||||
|
{
|
||||||
|
CBigNum bnResult;
|
||||||
|
bnResult.SetCompact(nBase);
|
||||||
|
while (nTime > 0 && bnResult < bnProofOfWorkLimit)
|
||||||
|
{
|
||||||
|
// Maximum 400% adjustment...
|
||||||
|
bnResult *= 4;
|
||||||
|
// ... in best-case exactly 4-times-normal target time
|
||||||
|
nTime -= nTargetTimespan*4;
|
||||||
|
}
|
||||||
|
if (bnResult > bnProofOfWorkLimit)
|
||||||
|
bnResult = bnProofOfWorkLimit;
|
||||||
|
return bnResult.GetCompact();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast)
|
unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast)
|
||||||
{
|
{
|
||||||
const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
|
||||||
const int64 nTargetSpacing = 10 * 60;
|
|
||||||
const int64 nInterval = nTargetTimespan / nTargetSpacing;
|
|
||||||
|
|
||||||
// Genesis block
|
// Genesis block
|
||||||
if (pindexLast == NULL)
|
if (pindexLast == NULL)
|
||||||
|
@ -1340,6 +1361,28 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
|
||||||
if (!pblock->CheckBlock())
|
if (!pblock->CheckBlock())
|
||||||
return error("ProcessBlock() : CheckBlock FAILED");
|
return error("ProcessBlock() : CheckBlock FAILED");
|
||||||
|
|
||||||
|
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
|
||||||
|
if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
|
||||||
|
{
|
||||||
|
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
|
||||||
|
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
|
||||||
|
if (deltaTime < 0)
|
||||||
|
{
|
||||||
|
pfrom->Misbehaving(100);
|
||||||
|
return error("ProcessBlock() : block with timestamp before last checkpoint");
|
||||||
|
}
|
||||||
|
CBigNum bnNewBlock;
|
||||||
|
bnNewBlock.SetCompact(pblock->nBits);
|
||||||
|
CBigNum bnRequired;
|
||||||
|
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
|
||||||
|
if (bnNewBlock > bnRequired)
|
||||||
|
{
|
||||||
|
pfrom->Misbehaving(100);
|
||||||
|
return error("ProcessBlock() : block with too little proof-of-work");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// If don't already have its previous block, shunt it off to holding area until we get it
|
// If don't already have its previous block, shunt it off to holding area until we get it
|
||||||
if (!mapBlockIndex.count(pblock->hashPrevBlock))
|
if (!mapBlockIndex.count(pblock->hashPrevBlock))
|
||||||
{
|
{
|
||||||
|
|
|
@ -99,6 +99,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
|
||||||
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
|
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
|
||||||
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
|
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
|
||||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
||||||
|
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
|
||||||
int GetNumBlocksOfPeers();
|
int GetNumBlocksOfPeers();
|
||||||
bool IsInitialBlockDownload();
|
bool IsInitialBlockDownload();
|
||||||
std::string GetWarnings(std::string strFor);
|
std::string GetWarnings(std::string strFor);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Unit tests for denial-of-service detection/prevention code
|
// Unit tests for denial-of-service detection/prevention code
|
||||||
//
|
//
|
||||||
|
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
@ -64,4 +65,54 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
||||||
BOOST_CHECK(!CNode::IsBanned(addr.ip));
|
BOOST_CHECK(!CNode::IsBanned(addr.ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)
|
||||||
|
{
|
||||||
|
if (time1 > time2)
|
||||||
|
return CheckNBits(nbits2, time2, nbits1, time1);
|
||||||
|
int64 deltaTime = time2-time1;
|
||||||
|
|
||||||
|
CBigNum required;
|
||||||
|
required.SetCompact(ComputeMinWork(nbits1, deltaTime));
|
||||||
|
CBigNum have;
|
||||||
|
have.SetCompact(nbits2);
|
||||||
|
return (have <= required);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(DoS_checknbits)
|
||||||
|
{
|
||||||
|
using namespace boost::assign; // for 'map_list_of()'
|
||||||
|
|
||||||
|
// Timestamps,nBits from the bitcoin blockchain.
|
||||||
|
// These are the block-chain checkpoint blocks
|
||||||
|
typedef std::map<int64, unsigned int> BlockData;
|
||||||
|
BlockData chainData =
|
||||||
|
map_list_of(1239852051,486604799)(1262749024,486594666)
|
||||||
|
(1279305360,469854461)(1280200847,469830746)(1281678674,469809688)
|
||||||
|
(1296207707,453179945)(1302624061,453036989)(1309640330,437004818)
|
||||||
|
(1313172719,436789733);
|
||||||
|
|
||||||
|
// Make sure CheckNBits considers every combination of block-chain-lock-in-points
|
||||||
|
// "sane":
|
||||||
|
BOOST_FOREACH(const BlockData::value_type& i, chainData)
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(const BlockData::value_type& j, chainData)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a couple of insane combinations:
|
||||||
|
BlockData::value_type firstcheck = *(chainData.begin());
|
||||||
|
BlockData::value_type lastcheck = *(chainData.rbegin());
|
||||||
|
|
||||||
|
// First checkpoint difficulty at or a while after the last checkpoint time should fail when
|
||||||
|
// compared to last checkpoint
|
||||||
|
BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first));
|
||||||
|
BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first));
|
||||||
|
|
||||||
|
// ... but OK if enough time passed for difficulty to adjust downward:
|
||||||
|
BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue