diff options
Diffstat (limited to 'src/validation.cpp')
-rw-r--r-- | src/validation.cpp | 72 |
1 files changed, 52 insertions, 20 deletions
diff --git a/src/validation.cpp b/src/validation.cpp index 0ad61ca729..bce8c4f9e9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -142,7 +142,7 @@ private: * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, * instead of putting things in this set. */ - std::set<CBlockIndex*> g_failed_blocks; + std::set<CBlockIndex*> m_failed_blocks; public: CChain chainActive; @@ -154,6 +154,10 @@ public: bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock); + /** + * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure + * that it doesn't descend from an invalid block, and then add it to mapBlockIndex. + */ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock); @@ -185,6 +189,11 @@ private: CBlockIndex* AddToBlockIndex(const CBlockHeader& block); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(const uint256& hash); + /** + * Make various assertions about the state of the block index. + * + * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex. + */ void CheckBlockIndex(const Consensus::Params& consensusParams); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); @@ -1286,7 +1295,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1615,22 +1624,27 @@ void static FlushBlockFile(bool fFinalize = false) LOCK(cs_LastBlockFile); CDiskBlockPos posOld(nLastBlockFile, 0); + bool status = true; FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + status &= FileCommit(fileOld); fclose(fileOld); } fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + status &= FileCommit(fileOld); fclose(fileOld); } + + if (!status) { + AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + } } static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); @@ -2201,14 +2215,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & void FlushStateToDisk() { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } void PruneAndFlush() { CValidationState state; fCheckForPruning = true; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } static void DoWarning(const std::string& strWarning) @@ -2645,6 +2663,10 @@ static void NotifyHeaderTip() { * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). + * + * ActivateBestChain is split into steps (see ActivateBestChainStep) so that + * we avoid holding cs_main for an extended period of time; the length of this + * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { // Note that while we're often called here from ProcessNewBlock, this is @@ -2803,7 +2825,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. @@ -2849,7 +2871,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } - g_failed_blocks.erase(it->second); + m_failed_blocks.erase(it->second); } it++; } @@ -3348,8 +3370,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + // If the previous block index isn't valid, determine if it descends from any block which + // has been found invalid (g_failed_blocks), then mark pindexPrev and any blocks + // between them as failed. if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { - for (const CBlockIndex* failedit : g_failed_blocks) { + for (const CBlockIndex* failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus & BLOCK_FAILED_VALID); CBlockIndex* invalid_walk = pindexPrev; @@ -3515,7 +3540,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); + return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state)); } } @@ -3523,7 +3548,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CValidationState state; // Only used to report errors, not invalidity - ignore it if (!g_chainstate.ActivateBestChain(state, chainparams, pblock)) - return error("%s: ActivateBestChain failed", __func__); + return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); return true; } @@ -3641,7 +3666,9 @@ void PruneBlockFilesManual(int nManualPruneHeight) { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } /** @@ -3895,6 +3922,7 @@ bool LoadChainTip(const CChainParams& chainparams) LogPrintf("%s: Connecting genesis block...\n", __func__); CValidationState state; if (!ActivateBestChain(state, chainparams)) { + LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state)); return false; } } @@ -4009,7 +4037,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) - return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } } @@ -4139,11 +4167,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) break; } if (!DisconnectTip(state, params, nullptr)) { - return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state)); } // Occasionally flush state to disk. - if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) + if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; + } } // Reduce validity flag and have-data flags. @@ -4209,6 +4239,7 @@ bool RewindBlockIndex(const CChainParams& params) { // it'll get called a bunch real soon. CValidationState state; if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } @@ -4218,7 +4249,7 @@ bool RewindBlockIndex(const CChainParams& params) { void CChainState::UnloadBlockIndex() { nBlockSequenceId = 1; - g_failed_blocks.clear(); + m_failed_blocks.clear(); setBlockIndexCandidates.clear(); } @@ -4295,7 +4326,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("%s: genesis block not accepted", __func__); + return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } @@ -4760,7 +4791,8 @@ bool DumpMempool(void) } file << mapDeltas; - FileCommit(file.Get()); + if (!FileCommit(file.Get())) + throw std::runtime_error("FileCommit failed"); file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); |