mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
UPSTREAM MERGE BROKEN: Merge upstream commit '519b0bc5dc' into master
This commit is contained in:
commit
f4889a5253
537 changed files with 31484 additions and 10750 deletions
|
|
@ -155,7 +155,7 @@ private:
|
|||
|
||||
public:
|
||||
CChain chainActive;
|
||||
BlockMap mapBlockIndex;
|
||||
BlockMap mapBlockIndex GUARDED_BY(cs_main);
|
||||
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
|
||||
CBlockIndex *pindexBestInvalid = nullptr;
|
||||
|
||||
|
|
@ -176,11 +176,11 @@ public:
|
|||
CCoinsViewCache& view, const CChainParams& chainparams, std::set<std::pair<uint256, COutPoint>>* setPeginsSpent, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
// Block disconnection on our pcoinsTip:
|
||||
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
|
||||
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
// Manual block validity manipulation:
|
||||
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
|
||||
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex);
|
||||
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
|
||||
|
|
@ -209,13 +209,23 @@ private:
|
|||
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
|
||||
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
//! Mark a block as not having block data
|
||||
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
} g_chainstate;
|
||||
|
||||
|
||||
|
||||
CCriticalSection cs_main;
|
||||
/**
|
||||
* Mutex to guard access to validation specific variables, such as reading
|
||||
* or changing the chainstate.
|
||||
*
|
||||
* This may also need to be locked when updating the transaction pool, e.g. on
|
||||
* AcceptToMemoryPool. See CTxMemPool::cs comment for details.
|
||||
*
|
||||
* The transaction pool has a separate lock to allow reading from it and the
|
||||
* chainstate at the same time.
|
||||
*/
|
||||
RecursiveMutex cs_main;
|
||||
|
||||
BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
|
||||
CChain& chainActive = g_chainstate.chainActive;
|
||||
|
|
@ -1059,13 +1069,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||
* Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
|
||||
* If blockIndex is provided, the transaction is fetched from the corresponding block.
|
||||
*/
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex)
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, const CBlockIndex* const block_index)
|
||||
{
|
||||
CBlockIndex* pindexSlow = blockIndex;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (!blockIndex) {
|
||||
if (!block_index) {
|
||||
CTransactionRef ptx = mempool.get(hash);
|
||||
if (ptx) {
|
||||
txOut = ptx;
|
||||
|
|
@ -1075,20 +1083,13 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
|
|||
if (g_txindex) {
|
||||
return g_txindex->FindTx(hash, hashBlock, txOut);
|
||||
}
|
||||
|
||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
||||
if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
|
||||
}
|
||||
}
|
||||
|
||||
if (pindexSlow) {
|
||||
} else {
|
||||
CBlock block;
|
||||
if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
|
||||
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (tx->GetHash() == hash) {
|
||||
txOut = tx;
|
||||
hashBlock = pindexSlow->GetBlockHash();
|
||||
hashBlock = block_index->GetBlockHash();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1796,8 +1797,7 @@ void ThreadScriptCheck() {
|
|||
scriptcheckqueue.Thread();
|
||||
}
|
||||
|
||||
// Protected by cs_main
|
||||
VersionBitsCache versionbitscache;
|
||||
VersionBitsCache versionbitscache GUARDED_BY(cs_main);
|
||||
|
||||
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
|
||||
{
|
||||
|
|
@ -1838,8 +1838,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Protected by cs_main
|
||||
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
|
||||
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
|
||||
|
||||
// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
|
||||
// mainnet. We no longer need to support disabling the segwit deployment
|
||||
|
|
@ -2520,14 +2519,11 @@ class ConnectTrace {
|
|||
private:
|
||||
std::vector<PerBlockConnectTrace> blocksConnected;
|
||||
CTxMemPool &pool;
|
||||
boost::signals2::scoped_connection m_connNotifyEntryRemoved;
|
||||
|
||||
public:
|
||||
explicit ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
|
||||
pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
|
||||
}
|
||||
|
||||
~ConnectTrace() {
|
||||
pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
|
||||
m_connNotifyEntryRemoved = pool.NotifyEntryRemoved.connect(std::bind(&ConnectTrace::NotifyEntryRemoved, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
|
||||
|
|
@ -2685,7 +2681,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
|
|||
CBlockIndex *pindexTest = pindexNew;
|
||||
bool fInvalidAncestor = false;
|
||||
while (pindexTest && !chainActive.Contains(pindexTest)) {
|
||||
assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
|
||||
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
|
||||
|
||||
// Pruned nodes may have entries in setBlockIndexCandidates for
|
||||
// which block files have been deleted. Remove those as candidates
|
||||
|
|
@ -2842,6 +2838,14 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
|
|||
}
|
||||
}
|
||||
|
||||
static void LimitValidationInterfaceQueue() {
|
||||
AssertLockNotHeld(cs_main);
|
||||
|
||||
if (GetMainSignals().CallbacksPending() > 10) {
|
||||
SyncWithValidationInterfaceQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -2870,15 +2874,13 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
|
|||
do {
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
if (GetMainSignals().CallbacksPending() > 10) {
|
||||
// Block until the validation queue drains. This should largely
|
||||
// never happen in normal operation, however may happen during
|
||||
// reindex, causing memory blowup if we run too far ahead.
|
||||
// Note that if a validationinterface callback ends up calling
|
||||
// ActivateBestChain this may lead to a deadlock! We should
|
||||
// probably have a DEBUG_LOCKORDER test for this in the future.
|
||||
SyncWithValidationInterfaceQueue();
|
||||
}
|
||||
// Block until the validation queue drains. This should largely
|
||||
// never happen in normal operation, however may happen during
|
||||
// reindex, causing memory blowup if we run too far ahead.
|
||||
// Note that if a validationinterface callback ends up calling
|
||||
// ActivateBestChain this may lead to a deadlock! We should
|
||||
// probably have a DEBUG_LOCKORDER test for this in the future.
|
||||
LimitValidationInterfaceQueue();
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
|
@ -2975,7 +2977,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
|
|||
// call preciousblock 2**31-1 times on the same set of tips...
|
||||
nBlockReverseSequenceId--;
|
||||
}
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) {
|
||||
setBlockIndexCandidates.insert(pindex);
|
||||
PruneBlockIndexCandidates();
|
||||
}
|
||||
|
|
@ -2989,64 +2991,85 @@ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIn
|
|||
|
||||
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
// We first disconnect backwards and then mark the blocks as invalid.
|
||||
// This prevents a case where pruned nodes may fail to invalidateblock
|
||||
// and be left unable to start as they have no tip candidates (as there
|
||||
// are no blocks that meet the "have data and are not invalid per
|
||||
// nStatus" criteria for inclusion in setBlockIndexCandidates).
|
||||
|
||||
CBlockIndex* to_mark_failed = pindex;
|
||||
bool pindex_was_in_chain = false;
|
||||
CBlockIndex *invalid_walk_tip = chainActive.Tip();
|
||||
int disconnected = 0;
|
||||
|
||||
DisconnectedBlockTransactions disconnectpool;
|
||||
while (chainActive.Contains(pindex)) {
|
||||
// Disconnect (descendants of) pindex, and mark them invalid.
|
||||
while (true) {
|
||||
if (ShutdownRequested()) break;
|
||||
|
||||
// Make sure the queue of validation callbacks doesn't grow unboundedly.
|
||||
LimitValidationInterfaceQueue();
|
||||
|
||||
LOCK(cs_main);
|
||||
if (!chainActive.Contains(pindex)) break;
|
||||
pindex_was_in_chain = true;
|
||||
CBlockIndex *invalid_walk_tip = chainActive.Tip();
|
||||
|
||||
// ActivateBestChain considers blocks already in chainActive
|
||||
// unconditionally valid already, so force disconnect away from it.
|
||||
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
|
||||
// It's probably hopeless to try to make the mempool consistent
|
||||
// here if DisconnectTip failed, but we can try.
|
||||
UpdateMempoolForReorg(disconnectpool, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DisconnectedBlockTransactions disconnectpool;
|
||||
bool ret = DisconnectTip(state, chainparams, &disconnectpool);
|
||||
// DisconnectTip will add transactions to disconnectpool.
|
||||
// Adjust the mempool to be consistent with the new tip, adding
|
||||
// transactions back to the mempool if disconnecting was succesful,
|
||||
// and we're not doing a very deep invalidation (in which case
|
||||
// keeping the mempool up to date is probably futile anyway).
|
||||
UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
|
||||
if (!ret) return false;
|
||||
assert(invalid_walk_tip->pprev == chainActive.Tip());
|
||||
|
||||
// Now mark the blocks we just disconnected as descendants invalid
|
||||
// (note this may not be all descendants).
|
||||
while (pindex_was_in_chain && invalid_walk_tip != pindex) {
|
||||
invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
|
||||
// We immediately mark the disconnected blocks as invalid.
|
||||
// This prevents a case where pruned nodes may fail to invalidateblock
|
||||
// and be left unable to start as they have no tip candidates (as there
|
||||
// are no blocks that meet the "have data and are not invalid per
|
||||
// nStatus" criteria for inclusion in setBlockIndexCandidates).
|
||||
invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
|
||||
setDirtyBlockIndex.insert(invalid_walk_tip);
|
||||
setBlockIndexCandidates.erase(invalid_walk_tip);
|
||||
invalid_walk_tip = invalid_walk_tip->pprev;
|
||||
}
|
||||
|
||||
// Mark the block itself as invalid.
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
setBlockIndexCandidates.erase(pindex);
|
||||
m_failed_blocks.insert(pindex);
|
||||
|
||||
// DisconnectTip will add transactions to disconnectpool; try to add these
|
||||
// back to the mempool.
|
||||
UpdateMempoolForReorg(disconnectpool, true);
|
||||
|
||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
|
||||
// add it again.
|
||||
BlockMap::iterator it = mapBlockIndex.begin();
|
||||
while (it != mapBlockIndex.end()) {
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
|
||||
setBlockIndexCandidates.insert(it->second);
|
||||
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
|
||||
if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
|
||||
// We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
|
||||
// need to be BLOCK_FAILED_CHILD instead.
|
||||
to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
|
||||
setDirtyBlockIndex.insert(to_mark_failed);
|
||||
}
|
||||
it++;
|
||||
|
||||
// Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
|
||||
// iterations, or, if it's the last one, call InvalidChainFound on it.
|
||||
to_mark_failed = invalid_walk_tip;
|
||||
}
|
||||
|
||||
InvalidChainFound(pindex);
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (chainActive.Contains(to_mark_failed)) {
|
||||
// If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark pindex (or the last disconnected block) as invalid, even when it never was in the main chain
|
||||
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
|
||||
setDirtyBlockIndex.insert(to_mark_failed);
|
||||
setBlockIndexCandidates.erase(to_mark_failed);
|
||||
m_failed_blocks.insert(to_mark_failed);
|
||||
|
||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
|
||||
// add it again.
|
||||
BlockMap::iterator it = mapBlockIndex.begin();
|
||||
while (it != mapBlockIndex.end()) {
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
|
||||
setBlockIndexCandidates.insert(it->second);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
InvalidChainFound(to_mark_failed);
|
||||
}
|
||||
|
||||
// Only notify about a new block tip if the active chain was modified.
|
||||
if (pindex_was_in_chain) {
|
||||
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
|
||||
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), to_mark_failed->pprev);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3066,7 +3089,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
|
|||
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
|
||||
it->second->nStatus &= ~BLOCK_FAILED_MASK;
|
||||
setDirtyBlockIndex.insert(it->second);
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
|
||||
setBlockIndexCandidates.insert(it->second);
|
||||
}
|
||||
if (it->second == pindexBestInvalid) {
|
||||
|
|
@ -3144,7 +3167,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
|
|||
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
|
||||
setDirtyBlockIndex.insert(pindexNew);
|
||||
|
||||
if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) {
|
||||
if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
|
||||
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
|
||||
std::deque<CBlockIndex*> queue;
|
||||
queue.push_back(pindexNew);
|
||||
|
|
@ -3554,6 +3577,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
|||
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
|
||||
int nLockTimeFlags = 0;
|
||||
if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
|
||||
assert(pindexPrev != nullptr);
|
||||
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
|
||||
}
|
||||
|
||||
|
|
@ -3673,10 +3697,30 @@ 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 (m_failed_blocks), then mark pindexPrev and any blocks
|
||||
// between them as failed.
|
||||
/* Determine if this block descends from any block which has been found
|
||||
* invalid (m_failed_blocks), then mark pindexPrev and any blocks between
|
||||
* them as failed. For example:
|
||||
*
|
||||
* D3
|
||||
* /
|
||||
* B2 - C2
|
||||
* / \
|
||||
* A D2 - E2 - F2
|
||||
* \
|
||||
* B1 - C1 - D1 - E1
|
||||
*
|
||||
* In the case that we attempted to reorg from E1 to F2, only to find
|
||||
* C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
|
||||
* but NOT D3 (it was not in any of our candidate sets at the time).
|
||||
*
|
||||
* In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
|
||||
* in LoadBlockIndex.
|
||||
*/
|
||||
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
|
||||
// The above does not mean "invalid": it checks if the previous block
|
||||
// hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance
|
||||
// optimization, in the common case of adding a new block to the tip,
|
||||
// we don't need to iterate over the failed blocks list.
|
||||
for (const CBlockIndex* failedit : m_failed_blocks) {
|
||||
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
|
||||
assert(failedit->nStatus & BLOCK_FAILED_VALID);
|
||||
|
|
@ -3831,12 +3875,14 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
|
|||
CBlockIndex *pindex = nullptr;
|
||||
if (fNewBlock) *fNewBlock = false;
|
||||
CValidationState state;
|
||||
|
||||
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
|
||||
// Therefore, the following critical section must include the CheckBlock() call as well.
|
||||
LOCK(cs_main);
|
||||
|
||||
// Ensure that CheckBlock() passes before calling AcceptBlock, as
|
||||
// belt-and-suspenders.
|
||||
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (ret) {
|
||||
// Store to disk
|
||||
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
|
||||
|
|
@ -4118,8 +4164,6 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
|
|||
if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
|
||||
return false;
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
// Calculate nChainWork
|
||||
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
|
||||
vSortedByHeight.reserve(mapBlockIndex.size());
|
||||
|
|
@ -4138,7 +4182,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
|
|||
// Pruned nodes may have deleted the block.
|
||||
if (pindex->nTx > 0) {
|
||||
if (pindex->pprev) {
|
||||
if (pindex->pprev->nChainTx) {
|
||||
if (pindex->pprev->HaveTxsDownloaded()) {
|
||||
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
|
||||
} else {
|
||||
pindex->nChainTx = 0;
|
||||
|
|
@ -4152,7 +4196,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
|
|||
pindex->nStatus |= BLOCK_FAILED_CHILD;
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
}
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))
|
||||
setBlockIndexCandidates.insert(pindex);
|
||||
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
|
||||
pindexBestInvalid = pindex;
|
||||
|
|
@ -4281,7 +4325,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
|
|||
LogPrintf("[0%%]..."); /* Continued */
|
||||
for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
|
||||
boost::this_thread::interruption_point();
|
||||
int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
|
||||
const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
|
||||
if (reportDone < percentageDone/10) {
|
||||
// report every 10% step
|
||||
LogPrintf("[%d%%]...", percentageDone); /* Continued */
|
||||
|
|
@ -4339,7 +4383,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
|
|||
if (nCheckLevel >= 4) {
|
||||
while (pindex != chainActive.Tip()) {
|
||||
boost::this_thread::interruption_point();
|
||||
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false);
|
||||
const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainActive.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..."), percentageDone, false);
|
||||
pindex = chainActive.Next(pindex);
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
|
||||
|
|
@ -4446,38 +4496,114 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
|
|||
return g_chainstate.ReplayBlocks(params, view);
|
||||
}
|
||||
|
||||
//! Helper for CChainState::RewindBlockIndex
|
||||
void CChainState::EraseBlockData(CBlockIndex* index)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
assert(!chainActive.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);
|
||||
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(index->pprev);
|
||||
while (ret.first != ret.second) {
|
||||
if (ret.first->second == index) {
|
||||
mapBlocksUnlinked.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)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
// Note that during -reindex-chainstate we are called with an empty chainActive!
|
||||
|
||||
int nHeight = 1;
|
||||
while (nHeight <= chainActive.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(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
|
||||
break;
|
||||
// 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 : mapBlockIndex) {
|
||||
if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) {
|
||||
EraseBlockData(entry.second);
|
||||
}
|
||||
}
|
||||
nHeight++;
|
||||
}
|
||||
|
||||
// Find what height we need to reorganize to.
|
||||
CBlockIndex *tip;
|
||||
int nHeight = 1;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
while (nHeight <= chainActive.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(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
|
||||
break;
|
||||
}
|
||||
nHeight++;
|
||||
}
|
||||
|
||||
tip = chainActive.Tip();
|
||||
}
|
||||
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
|
||||
|
||||
CValidationState state;
|
||||
CBlockIndex* pindex = chainActive.Tip();
|
||||
while (chainActive.Height() >= nHeight) {
|
||||
if (fPruneMode && !(chainActive.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;
|
||||
}
|
||||
if (!DisconnectTip(state, params, nullptr)) {
|
||||
return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state));
|
||||
// Loop until the tip is below nHeight, or we reach a pruned block.
|
||||
while (!ShutdownRequested()) {
|
||||
{
|
||||
LOCK(cs_main);
|
||||
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
|
||||
assert(tip == chainActive.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, FormatStateMessage(state));
|
||||
}
|
||||
|
||||
// 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 chainActive, 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 chainActive 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", FormatStateMessage(state));
|
||||
|
|
@ -4485,55 +4611,17 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
|
|||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
for (const auto& entry : mapBlockIndex) {
|
||||
CBlockIndex* pindexIter = entry.second;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (chainActive.Tip() != nullptr) {
|
||||
// We can't prune block index candidates based on our tip if we have
|
||||
// no tip due to chainActive being empty!
|
||||
PruneBlockIndexCandidates();
|
||||
|
||||
// Note: If we encounter an insufficiently validated block that
|
||||
// is on chainActive, 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 chainActive at this point
|
||||
// must not have their validity reduced.
|
||||
if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) {
|
||||
// Reduce validity
|
||||
pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
|
||||
// Remove have-data flags.
|
||||
pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
|
||||
// Remove storage location.
|
||||
pindexIter->nFile = 0;
|
||||
pindexIter->nDataPos = 0;
|
||||
pindexIter->nUndoPos = 0;
|
||||
// Remove various other things
|
||||
pindexIter->nTx = 0;
|
||||
pindexIter->nChainTx = 0;
|
||||
pindexIter->nSequenceId = 0;
|
||||
// Make sure it gets written.
|
||||
setDirtyBlockIndex.insert(pindexIter);
|
||||
// Update indexes
|
||||
setBlockIndexCandidates.erase(pindexIter);
|
||||
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
|
||||
while (ret.first != ret.second) {
|
||||
if (ret.first->second == pindexIter) {
|
||||
mapBlocksUnlinked.erase(ret.first++);
|
||||
} else {
|
||||
++ret.first;
|
||||
}
|
||||
}
|
||||
} else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) {
|
||||
setBlockIndexCandidates.insert(pindexIter);
|
||||
CheckBlockIndex(params.GetConsensus());
|
||||
}
|
||||
}
|
||||
|
||||
if (chainActive.Tip() != nullptr) {
|
||||
// We can't prune block index candidates based on our tip if we have
|
||||
// no tip due to chainActive being empty!
|
||||
PruneBlockIndexCandidates();
|
||||
|
||||
CheckBlockIndex(params.GetConsensus());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4625,7 +4713,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
|
|||
return true;
|
||||
|
||||
try {
|
||||
CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
|
||||
const CBlock& block = chainparams.GenesisBlock();
|
||||
CDiskBlockPos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
|
||||
if (blockPos.IsNull())
|
||||
return error("%s: writing genesis block to disk failed", __func__);
|
||||
|
|
@ -4822,7 +4910,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
|
|||
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
|
||||
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
|
||||
}
|
||||
if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
||||
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
||||
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
||||
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
||||
if (!fHavePruned) {
|
||||
|
|
@ -4835,9 +4923,9 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
|
|||
}
|
||||
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
|
||||
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
|
||||
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set.
|
||||
assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned).
|
||||
assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0));
|
||||
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
|
||||
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
|
||||
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
|
||||
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
|
||||
assert(pindex->pprev == nullptr || 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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue