mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Fix recovery from coin db crashes (and dbcrash.py test) (#3467)
* 🪲 improve evodb consistency recovering from dbcrash
* Adjust the fix
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
* Fix it
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
* Disable recovery from a crash during a fork and a corresponding part of dbcrash.py
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
* Skip some checks in CQuorumBlockProcessor when replaying blocks after the crash
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
* Process special txes in RollforwardBlock
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
* Update src/init.cpp
Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
This commit is contained in:
parent
d5f403d3fd
commit
017c4779ca
@ -1905,6 +1905,12 @@ bool AppInitMain()
|
|||||||
// The on-disk coinsdb is now in a good state, create the cache
|
// The on-disk coinsdb is now in a good state, create the cache
|
||||||
pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
|
pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
|
||||||
|
|
||||||
|
// flush evodb
|
||||||
|
if (!evoDb->CommitRootTransaction()) {
|
||||||
|
strLoadError = _("Failed to commit EvoDB");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
|
bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
|
||||||
if (!is_coinsview_empty) {
|
if (!is_coinsview_empty) {
|
||||||
// LoadChainTip sets chainActive based on pcoinsTip's best block
|
// LoadChainTip sets chainActive based on pcoinsTip's best block
|
||||||
|
@ -135,6 +135,11 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex*
|
|||||||
// until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
|
// until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
|
||||||
// allowed, including null commitments.
|
// allowed, including null commitments.
|
||||||
for (const auto& p : Params().GetConsensus().llmqs) {
|
for (const auto& p : Params().GetConsensus().llmqs) {
|
||||||
|
// skip these checks when replaying blocks after the crash
|
||||||
|
if (!chainActive.Tip()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
auto type = p.first;
|
auto type = p.first;
|
||||||
|
|
||||||
// does the currently processed block contain a (possibly null) commitment for the current session?
|
// does the currently processed block contain a (possibly null) commitment for the current session?
|
||||||
@ -180,6 +185,12 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH
|
|||||||
auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType);
|
auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType);
|
||||||
|
|
||||||
uint256 quorumHash = GetQuorumBlockHash((Consensus::LLMQType)qc.llmqType, nHeight);
|
uint256 quorumHash = GetQuorumBlockHash((Consensus::LLMQType)qc.llmqType, nHeight);
|
||||||
|
|
||||||
|
// skip `bad-qc-block` checks below when replaying blocks after the crash
|
||||||
|
if (!chainActive.Tip()) {
|
||||||
|
quorumHash = qc.quorumHash;
|
||||||
|
}
|
||||||
|
|
||||||
if (quorumHash.IsNull()) {
|
if (quorumHash.IsNull()) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
||||||
}
|
}
|
||||||
|
@ -1728,8 +1728,6 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move best block pointer to prevout block
|
|
||||||
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
|
||||||
|
|
||||||
if (fSpentIndex) {
|
if (fSpentIndex) {
|
||||||
if (!pblocktree->UpdateSpentIndex(spentIndex)) {
|
if (!pblocktree->UpdateSpentIndex(spentIndex)) {
|
||||||
@ -1749,6 +1747,8 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// move best block pointer to prevout block
|
||||||
|
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
||||||
evoDb->WriteBestBlock(pindex->pprev->GetBlockHash());
|
evoDb->WriteBestBlock(pindex->pprev->GetBlockHash());
|
||||||
|
|
||||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||||
@ -4322,6 +4322,13 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
|
|||||||
// Pass check = true as every addition may be an overwrite.
|
// Pass check = true as every addition may be an overwrite.
|
||||||
AddCoins(inputs, *tx, pindex->nHeight, true);
|
AddCoins(inputs, *tx, pindex->nHeight, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CValidationState state;
|
||||||
|
if (!ProcessSpecialTxsInBlock(block, pindex, state, false /*fJustCheck*/, false /*fScriptChecks*/)) {
|
||||||
|
return error("RollforwardBlock(DASH): ProcessSpecialTxsInBlock for block %s failed with %s",
|
||||||
|
pindex->GetBlockHash().ToString(), FormatStateMessage(state));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4356,8 +4363,13 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
|
|||||||
assert(pindexFork != nullptr);
|
assert(pindexFork != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto dbTx = evoDb->BeginTransaction();
|
||||||
|
|
||||||
// Rollback along the old branch.
|
// Rollback along the old branch.
|
||||||
while (pindexOld != pindexFork) {
|
while (pindexOld != pindexFork) {
|
||||||
|
// TODO: RollforwardBlock should update not only coins but also evodb and additional indexes.
|
||||||
|
// Disable recovery from a crash during a fork until this is implemented.
|
||||||
|
return error("ReplayBlocks(): recovery from a db crash during a fork is not supported yet");
|
||||||
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
|
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
|
||||||
CBlock block;
|
CBlock block;
|
||||||
if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) {
|
if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) {
|
||||||
@ -4385,7 +4397,10 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache.SetBestBlock(pindexNew->GetBlockHash());
|
cache.SetBestBlock(pindexNew->GetBlockHash());
|
||||||
cache.Flush();
|
evoDb->WriteBestBlock(pindexNew->GetBlockHash());
|
||||||
|
bool flushed = cache.Flush();
|
||||||
|
assert(flushed);
|
||||||
|
dbTx->Commit();
|
||||||
uiInterface.ShowProgress("", 100, false);
|
uiInterface.ShowProgress("", 100, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -229,14 +229,15 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
|
|||||||
self.generate_small_transactions(self.nodes[3], 2500, utxo_list)
|
self.generate_small_transactions(self.nodes[3], 2500, utxo_list)
|
||||||
# Pick a random block between current tip, and starting tip
|
# Pick a random block between current tip, and starting tip
|
||||||
current_height = self.nodes[3].getblockcount()
|
current_height = self.nodes[3].getblockcount()
|
||||||
random_height = random.randint(starting_tip_height, current_height)
|
# TODO: re-enable this when ReplayBlocks is fixed to support evodb and additional indexes
|
||||||
self.log.debug("At height %d, considering height %d", current_height, random_height)
|
# random_height = random.randint(starting_tip_height, current_height)
|
||||||
if random_height > starting_tip_height:
|
# self.log.debug("At height %d, considering height %d", current_height, random_height)
|
||||||
# Randomly reorg from this point with some probability (1/4 for
|
# if random_height > starting_tip_height:
|
||||||
# tip, 1/5 for tip-1, ...)
|
# # Randomly reorg from this point with some probability (1/4 for
|
||||||
if random.random() < 1.0/(current_height + 4 - random_height):
|
# # tip, 1/5 for tip-1, ...)
|
||||||
self.log.debug("Invalidating block at height %d", random_height)
|
# if random.random() < 1.0/(current_height + 4 - random_height):
|
||||||
self.nodes[3].invalidateblock(self.nodes[3].getblockhash(random_height))
|
# self.log.debug("Invalidating block at height %d", random_height)
|
||||||
|
# self.nodes[3].invalidateblock(self.nodes[3].getblockhash(random_height))
|
||||||
|
|
||||||
# Now generate new blocks until we pass the old tip height
|
# Now generate new blocks until we pass the old tip height
|
||||||
self.log.debug("Mining longer tip")
|
self.log.debug("Mining longer tip")
|
||||||
|
Loading…
Reference in New Issue
Block a user