Merge pull request #4554 from UdjinM6/bp15402_16849_15971

backport 15402, 16849, 15971
This commit is contained in:
UdjinM6 2021-11-01 18:26:11 +03:00 committed by GitHub
commit 12c399b3f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 202 additions and 114 deletions

View File

@ -1972,11 +1972,11 @@ bool AppInitMain(InitInterfaces& interfaces)
uiInterface.InitMessage(_("Loading block index..."));
LOCK(cs_main);
do {
const int64_t load_block_index_start_time = GetTimeMillis();
bool is_coinsview_empty;
try {
LOCK(cs_main);
// This statement makes ::ChainstateActive() usable.
g_chainstate = MakeUnique<CChainState>();
UnloadBlockIndex();
@ -2099,7 +2099,7 @@ bool AppInitMain(InitInterfaces& interfaces)
break;
}
bool is_coinsview_empty = fReset || fReindexChainState ||
is_coinsview_empty = fReset || fReindexChainState ||
::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
if (!is_coinsview_empty) {
// LoadChainTip initializes the chain based on CoinsTip()'s best block

View File

@ -509,43 +509,16 @@ void CChainLocksHandler::EnforceBestChainLock()
}
}
bool activateNeeded;
CValidationState state;
const auto &params = Params();
{
LOCK(cs_main);
// Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
// For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
// and mark all of them as conflicting.
while (pindex && !::ChainActive().Contains(pindex)) {
// Mark all blocks that have the same prevBlockHash but are not equal to blockHash as conflicting
auto itp = ::PrevBlockIndex().equal_range(pindex->pprev->GetBlockHash());
for (auto jt = itp.first; jt != itp.second; ++jt) {
if (jt->second == pindex) {
continue;
}
if (!MarkConflictingBlock(state, params, jt->second)) {
LogPrintf("CChainLocksHandler::%s -- MarkConflictingBlock failed: %s\n", __func__, FormatStateMessage(state));
// This should not have happened and we are in a state were it's not safe to continue anymore
assert(false);
}
LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) marked block %s as conflicting\n",
__func__, clsig->ToString(), jt->second->GetBlockHash().ToString());
}
// Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
// For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
// and mark all of them as conflicting.
LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, pindex->GetBlockHash().ToString(), clsig->ToString());
EnforceBlock(state, params, pindex);
pindex = pindex->pprev;
}
// In case blocks from the correct chain are invalid at the moment, reconsider them. The only case where this
// can happen right now is when missing superblock triggers caused the main chain to be dismissed first. When
// the trigger later appears, this should bring us to the correct chain eventually. Please note that this does
// NOT enforce invalid blocks in any way, it just causes re-validation.
if (!currentBestChainLockBlockIndex->IsValid()) {
ResetBlockFailureFlags(LookupBlockIndex(currentBestChainLockBlockIndex->GetBlockHash()));
}
activateNeeded = ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex;
}
bool activateNeeded = WITH_LOCK(::cs_main, return ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex;
if (activateNeeded) {
if(!ActivateBestChain(state, params)) {

View File

@ -1430,10 +1430,9 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const
LogPrintf("CInstantSendManager::%s -- invalidating block %s\n", __func__, pindex->GetBlockHash().ToString());
LOCK(cs_main);
CValidationState state;
// need non-const pointer
auto pindex2 = LookupBlockIndex(pindex->GetBlockHash());
auto pindex2 = WITH_LOCK(::cs_main, return LookupBlockIndex(pindex->GetBlockHash()));
if (!InvalidateBlock(state, Params(), pindex2)) {
LogPrintf("CInstantSendManager::%s -- InvalidateBlock failed: %s\n", __func__, FormatStateMessage(state));
// This should not have happened and we are in a state were it's not safe to continue anymore

View File

@ -1778,15 +1778,15 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
uint256 hash(uint256S(strHash));
CValidationState state;
CBlockIndex* pblockindex;
{
LOCK(cs_main);
CBlockIndex* pblockindex = LookupBlockIndex(hash);
pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
InvalidateBlock(state, Params(), pblockindex);
}
InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
ActivateBestChain(state, Params());

View File

@ -518,20 +518,22 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// Mine an empty block
createAndProcessEmptyBlock();
{
LOCK(cs_main);
SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
} // unlock cs_main while calling InvalidateBlock
CValidationState state;
InvalidateBlock(state, chainparams, ::ChainActive().Tip());
InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip()));
SetMockTime(0);
mempool.clear();
LOCK(::mempool.cs);
LOCK2(cs_main, ::mempool.cs);
TestPackageSelection(chainparams, scriptPubKey, txFirst);
fCheckpointsEnabled = true;

View File

@ -3034,6 +3034,14 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
}
}
static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
AssertLockNotHeld(cs_main);
if (GetMainSignals().CallbacksPending() > 10) {
SyncWithValidationInterfaceQueue();
}
}
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
// far from a guarantee. Things in the P2P/RPC will often end up calling
@ -3055,15 +3063,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);
@ -3181,76 +3187,148 @@ 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 = m_chain.Tip();
int disconnected = 0;
if (pindex == pindexBestHeader) {
pindexBestInvalid = pindexBestHeader;
pindexBestHeader = pindexBestHeader->pprev;
// We do not allow ActivateBestChain() to run while InvalidateBlock() is
// running, as that could cause the tip to change while we disconnect
// blocks.
LOCK(m_cs_chainstate);
// We'll be acquiring and releasing cs_main below, to allow the validation
// callbacks to run. However, we should keep the block index in a
// consistent state as we disconnect blocks -- in particular we need to
// add equal-work blocks to setBlockIndexCandidates as we disconnect.
// To avoid walking the block index repeatedly in search of candidates,
// build a map once so that we can look up candidate blocks by chain
// work as we go.
std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work;
{
LOCK(cs_main);
for (const auto& entry : m_blockman.m_block_index) {
CBlockIndex *candidate = entry.second;
// We don't need to put anything in our active chain into the
// multimap, because those candidates will be found and considered
// as we disconnect.
// Instead, consider only non-active-chain blocks that have at
// least as much work as where we expect the new tip to end up.
if (!m_chain.Contains(candidate) &&
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&
candidate->HaveTxsDownloaded()) {
candidate_blocks_by_work.insert(std::make_pair(candidate->nChainWork, candidate));
}
}
}
DisconnectedBlockTransactions disconnectpool;
while (m_chain.Contains(pindex)) {
const CBlockIndex* pindexOldTip = m_chain.Tip();
// 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 (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
const CBlockIndex* pindexOldTip = m_chain.Tip();
if (pindex == pindexBestHeader) {
pindexBestInvalid = pindexBestHeader;
pindexBestHeader = pindexBestHeader->pprev;
}
// ActivateBestChain considers blocks already in m_chain
// 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 == m_chain.Tip());
if (pindexOldTip == pindexBestHeader) {
pindexBestInvalid = pindexBestHeader;
pindexBestHeader = pindexBestHeader->pprev;
}
}
// 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_blockman.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 = g_blockman.m_block_index.begin();
while (it != g_blockman.m_block_index.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && !(it->second->nStatus & BLOCK_CONFLICT_CHAINLOCK) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.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++;
// Add any equal or more work headers to setBlockIndexCandidates
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->nChainWork);
while (candidate_it != candidate_blocks_by_work.end()) {
if (!CBlockIndexWorkComparator()(candidate_it->second, invalid_walk_tip->pprev)) {
setBlockIndexCandidates.insert(candidate_it->second);
candidate_it = candidate_blocks_by_work.erase(candidate_it);
} else {
++candidate_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);
GetMainSignals().SynchronousUpdatedBlockTip(m_chain.Tip(), nullptr, IsInitialBlockDownload());
GetMainSignals().UpdatedBlockTip(m_chain.Tip(), nullptr, IsInitialBlockDownload());
CheckBlockIndex(chainparams.GetConsensus());
{
LOCK(cs_main);
if (m_chain.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_blockman.m_failed_blocks.insert(to_mark_failed);
// If any new blocks somehow arrived while we were disconnecting
// (above), then the pre-calculation of what should go into
// setBlockIndexCandidates may have missed entries. This would
// technically be an inconsistency in the block index, but if we clean
// it up here, this should be an essentially unobservable error.
// Loop back over all block index entries and add any missing entries
// to setBlockIndexCandidates.
BlockMap::iterator it = g_blockman.m_block_index.begin();
while (it != g_blockman.m_block_index.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && !(it->second->nStatus & BLOCK_CONFLICT_CHAINLOCK) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
it++;
}
InvalidChainFound(to_mark_failed);
GetMainSignals().SynchronousUpdatedBlockTip(m_chain.Tip(), nullptr, IsInitialBlockDownload());
GetMainSignals().UpdatedBlockTip(m_chain.Tip(), nullptr, IsInitialBlockDownload());
}
// 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;
}
@ -3259,6 +3337,41 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
}
void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex)
{
AssertLockNotHeld(::cs_main);
LOCK2(m_cs_chainstate, ::cs_main);
const CBlockIndex* pindex_walk = pindex;
while (pindex_walk && !::ChainActive().Contains(pindex_walk)) {
// Mark all blocks that have the same prevBlockHash but are not equal to blockHash as conflicting
auto itp = ::PrevBlockIndex().equal_range(pindex_walk->pprev->GetBlockHash());
for (auto jt = itp.first; jt != itp.second; ++jt) {
if (jt->second == pindex_walk) {
continue;
}
if (!MarkConflictingBlock(state, chainparams, jt->second)) {
LogPrintf("CChainState::%s -- MarkConflictingBlock failed: %s\n", __func__, FormatStateMessage(state));
// This should not have happened and we are in a state were it's not safe to continue anymore
assert(false);
}
LogPrintf("CChainState::%s -- marked block %s as conflicting\n",
__func__, jt->second->GetBlockHash().ToString());
}
pindex_walk = pindex_walk->pprev;
}
// In case blocks from the enforced chain are invalid at the moment, reconsider them.
if (!pindex->IsValid()) {
ResetBlockFailureFlags(LookupBlockIndex(pindex->GetBlockHash()));
}
}
void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) {
return ::ChainstateActive().EnforceBlock(state, chainparams, pindex);
}
bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
AssertLockHeld(cs_main);
@ -3326,10 +3439,6 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
return true;
}
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
return ::ChainstateActive().MarkConflictingBlock(state, chainparams, pindex);
}
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);

View File

@ -240,7 +240,7 @@ bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::P
* May not be called with cs_main held. May not be called in a
* validationinterface callback.
*/
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()) LOCKS_EXCLUDED(cs_main);
double ConvertBitsToDouble(unsigned int nBits);
CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false);
@ -463,7 +463,6 @@ public:
CBlockTreeDB& blocktree,
std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -679,7 +678,7 @@ public:
bool ActivateBestChain(
CValidationState& state,
const CChainParams& chainparams,
std::shared_ptr<const CBlock> pblock);
std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -692,8 +691,8 @@ public:
// 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 MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Replay blocks that aren't fully applied to the database. */
@ -739,6 +738,12 @@ private:
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Mark a block as conflicting
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Mark a block as not having block data
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
/** Mark a block as precious and reorganize.
@ -749,10 +754,10 @@ private:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as conflicting. */
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Enforce a block marking all the other chains as conflicting. */
void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);