Browse Source

Merge pull request #5900

3fcfbc8 Add a consistency check for the block chain data structures (Pieter Wuille)
0.13
Wladimir J. van der Laan 10 years ago
parent
commit
3e8a1f2725
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 6
      src/chainparams.cpp
  2. 6
      src/chainparams.h
  3. 5
      src/init.cpp
  4. 146
      src/main.cpp
  5. 1
      src/main.h
  6. 1
      src/test/test_bitcoin.cpp

6
src/chainparams.cpp

@ -167,7 +167,7 @@ public:
fRequireRPCPassword = true; fRequireRPCPassword = true;
fMiningRequiresPeers = true; fMiningRequiresPeers = true;
fDefaultCheckMemPool = false; fDefaultConsistencyChecks = false;
fRequireStandard = true; fRequireStandard = true;
fMineBlocksOnDemand = false; fMineBlocksOnDemand = false;
fTestnetToBeDeprecatedFieldRPC = false; fTestnetToBeDeprecatedFieldRPC = false;
@ -222,7 +222,7 @@ public:
fRequireRPCPassword = true; fRequireRPCPassword = true;
fMiningRequiresPeers = true; fMiningRequiresPeers = true;
fDefaultCheckMemPool = false; fDefaultConsistencyChecks = false;
fRequireStandard = false; fRequireStandard = false;
fMineBlocksOnDemand = false; fMineBlocksOnDemand = false;
fTestnetToBeDeprecatedFieldRPC = true; fTestnetToBeDeprecatedFieldRPC = true;
@ -263,7 +263,7 @@ public:
fRequireRPCPassword = false; fRequireRPCPassword = false;
fMiningRequiresPeers = false; fMiningRequiresPeers = false;
fDefaultCheckMemPool = true; fDefaultConsistencyChecks = true;
fRequireStandard = false; fRequireStandard = false;
fMineBlocksOnDemand = true; fMineBlocksOnDemand = true;
fTestnetToBeDeprecatedFieldRPC = false; fTestnetToBeDeprecatedFieldRPC = false;

6
src/chainparams.h

@ -57,8 +57,8 @@ public:
bool RequireRPCPassword() const { return fRequireRPCPassword; } bool RequireRPCPassword() const { return fRequireRPCPassword; }
/** Make miner wait to have peers to avoid wasting work */ /** Make miner wait to have peers to avoid wasting work */
bool MiningRequiresPeers() const { return fMiningRequiresPeers; } bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
/** Default value for -checkmempool argument */ /** Default value for -checkmempool and -checkblockindex argument */
bool DefaultCheckMemPool() const { return fDefaultCheckMemPool; } bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
/** Allow mining of a min-difficulty block */ /** Allow mining of a min-difficulty block */
bool AllowMinDifficultyBlocks() const { return consensus.fPowAllowMinDifficultyBlocks; } bool AllowMinDifficultyBlocks() const { return consensus.fPowAllowMinDifficultyBlocks; }
/** Make standard checks */ /** Make standard checks */
@ -92,7 +92,7 @@ protected:
std::vector<CAddress> vFixedSeeds; std::vector<CAddress> vFixedSeeds;
bool fRequireRPCPassword; bool fRequireRPCPassword;
bool fMiningRequiresPeers; bool fMiningRequiresPeers;
bool fDefaultCheckMemPool; bool fDefaultConsistencyChecks;
bool fRequireStandard; bool fRequireStandard;
bool fMineBlocksOnDemand; bool fMineBlocksOnDemand;
bool fTestnetToBeDeprecatedFieldRPC; bool fTestnetToBeDeprecatedFieldRPC;

5
src/init.cpp

@ -697,8 +697,9 @@ bool AppInit2(boost::thread_group& threadGroup)
if (GetBoolArg("-benchmark", false)) if (GetBoolArg("-benchmark", false))
InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench.")); InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench."));
// Checkmempool defaults to true in regtest mode // Checkmempool and checkblockindex default to true in regtest mode
mempool.setSanityCheck(GetBoolArg("-checkmempool", Params().DefaultCheckMemPool())); mempool.setSanityCheck(GetBoolArg("-checkmempool", Params().DefaultConsistencyChecks()));
fCheckBlockIndex = GetBoolArg("-checkblockindex", Params().DefaultConsistencyChecks());
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency

146
src/main.cpp

@ -53,6 +53,7 @@ bool fImporting = false;
bool fReindex = false; bool fReindex = false;
bool fTxIndex = false; bool fTxIndex = false;
bool fIsBareMultisigStd = true; bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
unsigned int nCoinCacheSize = 5000; unsigned int nCoinCacheSize = 5000;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
@ -74,6 +75,7 @@ void EraseOrphansFor(NodeId peer);
* and going backwards. * and going backwards.
*/ */
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired); static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired);
static void CheckBlockIndex();
/** Constant stuff for coinbase transactions we create: */ /** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS; CScript COINBASE_FLAGS;
@ -85,7 +87,7 @@ namespace {
struct CBlockIndexWorkComparator struct CBlockIndexWorkComparator
{ {
bool operator()(CBlockIndex *pa, CBlockIndex *pb) { bool operator()(CBlockIndex *pa, CBlockIndex *pb) const {
// First sort by most total work, ... // First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true; if (pa->nChainWork < pb->nChainWork) return true;
@ -107,8 +109,8 @@ namespace {
CBlockIndex *pindexBestInvalid; CBlockIndex *pindexBestInvalid;
/** /**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip. Entries may be failed, though. * as good as our current tip or better. Entries may be failed, though.
*/ */
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** Number of nodes with fSyncStarted. */ /** Number of nodes with fSyncStarted. */
@ -2226,6 +2228,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
uiInterface.NotifyBlockTip(hashNewTip); uiInterface.NotifyBlockTip(hashNewTip);
} }
} while(pindexMostWork != chainActive.Tip()); } while(pindexMostWork != chainActive.Tip());
CheckBlockIndex();
// Write changes periodically to disk, after relay. // Write changes periodically to disk, after relay.
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
@ -2362,7 +2365,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
CBlockIndex *pindex = queue.front(); CBlockIndex *pindex = queue.front();
queue.pop_front(); queue.pop_front();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
setBlockIndexCandidates.insert(pindex); if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex); std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex);
while (range.first != range.second) { while (range.first != range.second) {
std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first; std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first;
@ -2725,6 +2730,7 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis
if (pindex && pfrom) { if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
} }
CheckBlockIndex();
if (!ret) if (!ret)
return error("%s: AcceptBlock FAILED", __func__); return error("%s: AcceptBlock FAILED", __func__);
} }
@ -3213,6 +3219,136 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
return nLoaded > 0; return nLoaded > 0;
} }
void static CheckBlockIndex()
{
if (!fCheckBlockIndex) {
return;
}
LOCK(cs_main);
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward;
for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
forward.insert(std::make_pair(it->second->pprev, it->second));
}
assert(forward.size() == mapBlockIndex.size());
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(NULL);
CBlockIndex *pindex = rangeGenesis.first->second;
rangeGenesis.first++;
assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL.
// Iterate over the entire block tree, using depth-first search.
// Along the way, remember whether there are blocks on the path from genesis
// block being explored which are the first to have certain properties.
size_t nNodes = 0;
int nHeight = 0;
CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid.
CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA.
CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not).
CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not).
CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not).
while (pindex != NULL) {
nNodes++;
if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex;
if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex;
if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex;
if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex;
// Begin: actual consistency checks.
if (pindex->pprev == NULL) {
// Genesis block checks.
assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match.
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
}
assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available.
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid
if (pindexFirstInvalid == NULL) {
// Checks for not-invalid blocks.
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
}
if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) {
if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates.
assert(setBlockIndexCandidates.count(pindex));
}
} else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates.
assert(setBlockIndexCandidates.count(pindex) == 0);
}
// Check whether this block is in mapBlocksUnlinked.
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev);
bool foundInUnlinked = false;
while (rangeUnlinked.first != rangeUnlinked.second) {
assert(rangeUnlinked.first->first == pindex->pprev);
if (rangeUnlinked.first->second == pindex) {
foundInUnlinked = true;
break;
}
rangeUnlinked.first++;
}
if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) {
if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked.
assert(foundInUnlinked);
}
} else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked.
assert(!foundInUnlinked);
}
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
// End: actual consistency checks.
// Try descending into the first subnode.
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> range = forward.equal_range(pindex);
if (range.first != range.second) {
// A subnode was found.
pindex = range.first->second;
nHeight++;
continue;
}
// This is a leaf node.
// Move upwards until we reach a node of which we have not yet visited the last child.
while (pindex) {
// We are going to either move to a parent or a sibling of pindex.
// If pindex was the first with a certain property, unset the corresponding variable.
if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL;
if (pindex == pindexFirstMissing) pindexFirstMissing = NULL;
if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL;
if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL;
if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL;
// Find our parent.
CBlockIndex* pindexPar = pindex->pprev;
// Find which child we just visited.
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangePar = forward.equal_range(pindexPar);
while (rangePar.first->second != pindex) {
assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child.
rangePar.first++;
}
// Proceed to the next one.
rangePar.first++;
if (rangePar.first != rangePar.second) {
// Move to the sibling.
pindex = rangePar.first->second;
break;
} else {
// Move up further.
pindex = pindexPar;
nHeight--;
continue;
}
}
}
// Check that we actually traversed the entire map.
assert(nNodes == forward.size());
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// CAlert // CAlert
@ -3971,6 +4107,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight);
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256());
} }
CheckBlockIndex();
} }
else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing

1
src/main.h

@ -122,6 +122,7 @@ extern bool fReindex;
extern int nScriptCheckThreads; extern int nScriptCheckThreads;
extern bool fTxIndex; extern bool fTxIndex;
extern bool fIsBareMultisigStd; extern bool fIsBareMultisigStd;
extern bool fCheckBlockIndex;
extern unsigned int nCoinCacheSize; extern unsigned int nCoinCacheSize;
extern CFeeRate minRelayTxFee; extern CFeeRate minRelayTxFee;

1
src/test/test_bitcoin.cpp

@ -30,6 +30,7 @@ BasicTestingSetup::BasicTestingSetup()
{ {
SetupEnvironment(); SetupEnvironment();
fPrintToDebugLog = false; // don't want to write to debug.log file fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} }
BasicTestingSetup::~BasicTestingSetup() BasicTestingSetup::~BasicTestingSetup()

Loading…
Cancel
Save