aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp194
1 files changed, 42 insertions, 152 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 3ad53fbcb6..bf774a0791 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2204,7 +2204,7 @@ bool CChainState::FlushStateToDisk(
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
- if (!CheckDiskSpace(GetBlocksDir())) {
+ if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
{
@@ -3890,12 +3890,12 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
static FlatFileSeq BlockFileSeq()
{
- return FlatFileSeq(GetBlocksDir(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
+ return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
}
static FlatFileSeq UndoFileSeq()
{
- return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE);
+ return FlatFileSeq(gArgs.GetBlocksDirPath(), "rev", UNDOFILE_CHUNK_SIZE);
}
FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) {
@@ -4101,38 +4101,46 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100, false);
}
-bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(
+ CChainState& chainstate,
+ const CChainParams& chainparams,
+ CCoinsView& coinsview,
+ int nCheckLevel, int nCheckDepth)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
- if (active_chainstate.m_chain.Tip() == nullptr || active_chainstate.m_chain.Tip()->pprev == nullptr)
+ assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+ if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height())
- nCheckDepth = active_chainstate.m_chain.Height();
+ if (nCheckDepth <= 0 || nCheckDepth > chainstate.m_chain.Height())
+ nCheckDepth = chainstate.m_chain.Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
- CCoinsViewCache coins(coinsview);
+ CCoinsViewCache coins(&coinsview);
CBlockIndex* pindex;
CBlockIndex* pindexFailure = nullptr;
int nGoodTransactions = 0;
BlockValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (pindex = active_chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
- const int percentageDone = std::max(1, std::min(99, (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+
+ bool is_snapshot_cs = !chainstate.m_from_snapshot_blockhash.IsNull();
+
+ for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- if (pindex->nHeight <= active_chainstate.m_chain.Height()-nCheckDepth)
+ if (pindex->nHeight <= chainstate.m_chain.Height()-nCheckDepth)
break;
- if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, only go back as far as we have data.
+ if ((fPruneMode || is_snapshot_cs) && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
+ // If pruning or running under an assumeutxo snapshot, only go
+ // back as far as we have data.
LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight);
break;
}
@@ -4154,9 +4162,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_ch
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) {
+ size_t curr_coins_usage = coins.DynamicMemoryUsage() + chainstate.CoinsTip().DynamicMemoryUsage();
+
+ if (nCheckLevel >= 3 && curr_coins_usage <= chainstate.m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4170,26 +4180,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_ch
if (ShutdownRequested()) return true;
}
if (pindexFailure)
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", active_chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = active_chainstate.m_chain.Height() - pindex->nHeight;
+ int block_count = chainstate.m_chain.Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- while (pindex != active_chainstate.m_chain.Tip()) {
- const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ while (pindex != chainstate.m_chain.Tip()) {
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- pindex = active_chainstate.m_chain.Next(pindex);
+ pindex = chainstate.m_chain.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
if (ShutdownRequested()) return true;
}
@@ -4289,143 +4299,23 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
return true;
}
-//! Helper for CChainState::RewindBlockIndex
-void CChainState::EraseBlockData(CBlockIndex* index)
+bool CChainState::NeedsRedownload(const CChainParams& params) const
{
AssertLockHeld(cs_main);
- assert(!m_chain.Contains(index)); // Make sure this block isn't active
-
- // Reduce validity
- index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
- // Remove have-data flags.
- index->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
- // Remove storage location.
- index->nFile = 0;
- index->nDataPos = 0;
- index->nUndoPos = 0;
- // Remove various other things
- index->nTx = 0;
- index->nChainTx = 0;
- index->nSequenceId = 0;
- // Make sure it gets written.
- setDirtyBlockIndex.insert(index);
- // Update indexes
- setBlockIndexCandidates.erase(index);
- auto ret = m_blockman.m_blocks_unlinked.equal_range(index->pprev);
- while (ret.first != ret.second) {
- if (ret.first->second == index) {
- m_blockman.m_blocks_unlinked.erase(ret.first++);
- } else {
- ++ret.first;
- }
- }
- // Mark parent as eligible for main chain again
- if (index->pprev && index->pprev->IsValid(BLOCK_VALID_TRANSACTIONS) && index->pprev->HaveTxsDownloaded()) {
- setBlockIndexCandidates.insert(index->pprev);
- }
-}
-
-bool CChainState::RewindBlockIndex(const CChainParams& params)
-{
- // Note that during -reindex-chainstate we are called with an empty m_chain!
-
- // First erase all post-segwit blocks without witness not in the main chain,
- // as this can we done without costly DisconnectTip calls. Active
- // blocks will be dealt with below (releasing cs_main in between).
- {
- LOCK(cs_main);
- for (const auto& entry : m_blockman.m_block_index) {
- if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
- EraseBlockData(entry.second);
- }
- }
- }
-
- // Find what height we need to reorganize to.
- CBlockIndex *tip;
- int nHeight = 1;
- {
- LOCK(cs_main);
- while (nHeight <= m_chain.Height()) {
- // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
- // blocks in ConnectBlock, we don't need to go back and
- // re-download/re-verify blocks from before segwit actually activated.
- if (IsWitnessEnabled(m_chain[nHeight - 1], params.GetConsensus()) && !(m_chain[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
- break;
- }
- nHeight++;
- }
- tip = m_chain.Tip();
- }
- // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
+ // At and above params.SegwitHeight, segwit consensus rules must be validated
+ CBlockIndex* block{m_chain.Tip()};
+ const int segwit_height{params.GetConsensus().SegwitHeight};
- BlockValidationState state;
- // Loop until the tip is below nHeight, or we reach a pruned block.
- while (!ShutdownRequested()) {
- {
- LOCK(cs_main);
- LOCK(m_mempool.cs);
- // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
- assert(tip == m_chain.Tip());
- if (tip == nullptr || tip->nHeight < nHeight) break;
- if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, don't try rewinding past the HAVE_DATA point;
- // since older blocks can't be served anyway, there's
- // no need to walk further, and trying to DisconnectTip()
- // will fail (and require a needless reindex/redownload
- // of the blockchain).
- break;
- }
-
- // Disconnect block
- if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, state.ToString());
- }
-
- // Reduce validity flag and have-data flags.
- // We do this after actual disconnecting, otherwise we'll end up writing the lack of data
- // to disk before writing the chainstate, resulting in a failure to continue if interrupted.
- // Note: If we encounter an insufficiently validated block that
- // is on m_chain, it must be because we are a pruning node, and
- // this block or some successor doesn't HAVE_DATA, so we were unable to
- // rewind all the way. Blocks remaining on m_chain at this point
- // must not have their validity reduced.
- EraseBlockData(tip);
-
- tip = tip->pprev;
- }
- // Make sure the queue of validation callbacks doesn't grow unboundedly.
- LimitValidationInterfaceQueue();
-
- // Occasionally flush state to disk.
- if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
- return false;
- }
- }
-
- {
- LOCK(cs_main);
- if (m_chain.Tip() != nullptr) {
- // We can't prune block index candidates based on our tip if we have
- // no tip due to m_chain being empty!
- PruneBlockIndexCandidates();
-
- CheckBlockIndex(params.GetConsensus());
-
- // FlushStateToDisk can possibly read ::ChainActive(). Be conservative
- // and skip it here, we're about to -reindex-chainstate anyway, so
- // it'll get called a bunch real soon.
- BlockValidationState state;
- if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
- return false;
- }
+ while (block != nullptr && block->nHeight >= segwit_height) {
+ if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
+ // block is insufficiently validated for a segwit client
+ return true;
}
+ block = block->pprev;
}
- return true;
+ return false;
}
void CChainState::UnloadBlockIndex() {