diff --git a/src/init.cpp b/src/init.cpp index be7c8a1399cb4c..816bb2e3922659 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -421,6 +421,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks"); strUsage += HelpMessageOpt("-reindex", "Rebuild block chain index from current blk000??.dat files on startup"); strUsage += HelpMessageOpt("-resync", "Delete blockchain folders and resync from scratch on startup"); + strUsage += HelpMessageOpt("-rewindblockindex", "Rewind blockchain to the last checkpoint"); #if !defined(WIN32) strUsage += HelpMessageOpt("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"); #endif @@ -1424,6 +1425,7 @@ bool AppInitMain() fReindex = gArgs.GetBoolArg("-reindex", false); bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false); + bool clearWitnessCaches = false; // cache size calculations int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); @@ -1572,8 +1574,17 @@ bool AppInitMain() } } + uiInterface.InitMessage(_("Verifying blocks...")); + if (!is_coinsview_empty) { - uiInterface.InitMessage(_("Verifying blocks...")); + if (!fReindex && chainActive.Tip() != NULL) { + uiInterface.InitMessage(_("Rewinding blocks to last checkpoint if needed...")); + if (!RewindBlockIndexToLastCheckpoint(chainparams, clearWitnessCaches, gArgs.GetBoolArg("-rewindblockindex", false))) { + strLoadError = _("Unable to rewind the blockchain to last checkpoint. You will need to redownload the blockchain"); + break; + } + } + CBlockIndex *tip = chainActive.Tip(); RPCNotifyBlockChange(true, tip); if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { @@ -1635,7 +1646,7 @@ bool AppInitMain() // ********************************************************* Step 8: Backup and Load wallet #ifdef ENABLE_WALLET - if (!InitLoadWallet()) + if (!InitLoadWallet(clearWitnessCaches)) return false; #else LogPrintf("No wallet compiled in!\n"); diff --git a/src/txdb.cpp b/src/txdb.cpp index f971f1389edce7..58d5341c51bf9d 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -247,6 +247,14 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector& blockinfo) { + CDBBatch batch; + for (std::vector::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { + batch.Erase(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash())); + } + return WriteBatch(batch, true); +} + bool CBlockTreeDB::ReadTxIndex(const uint256& txid, CDiskTxPos& pos) { return Read(std::make_pair(DB_TXINDEX, txid), pos); diff --git a/src/txdb.h b/src/txdb.h index 3dce71b61a274f..22d278f95243af 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -134,6 +134,7 @@ class CBlockTreeDB : public CDBWrapper bool WriteBlockIndex(const CDiskBlockIndex& blockindex); bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); + bool EraseBatchSync(const std::vector& blockinfo); bool ReadBlockFileInfo(int nFile, CBlockFileInfo& info); bool ReadLastBlockFile(int& nFile); bool WriteReindexing(bool fReindexing); diff --git a/src/validation.cpp b/src/validation.cpp index 20399aa5308f39..d0eeb8b933092a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3741,6 +3741,102 @@ bool CVerifyDB::VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth return true; } +bool RewindBlockIndexToLastCheckpoint(const CChainParams& chainparams, bool& clearWitnessCaches, bool fJustCheck) +{ + LOCK(cs_main); + + // Get current Height and current Checkpoints + int nHeight = chainActive.Height(); + const CBlockIndex* prevCheckPoint = GetLastCheckpoint(); + const int checkPointHeight = prevCheckPoint ? prevCheckPoint->nHeight : 0; + + // First load options + if(!fJustCheck) + return true; + + clearWitnessCaches = true; + CValidationState state; + const int blocksToRollBack = nHeight - checkPointHeight; + // Iterate to start removing blocks + while (nHeight > checkPointHeight) { + if (!DisconnectTip(state, chainparams, nullptr)) { + return error("%s: unable to disconnect block at height %i", __func__, nHeight); + } + // Occasionally flush state to disk. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) + return false; + + nHeight = chainActive.Height(); + } + + // Collect blocks to be removed (blocks in mapBlockIndex must be at least BLOCK_VALID_TREE). + // 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. + std::vector vBlocks; + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + CBlockIndex* pindexIter = it->second; + if (!chainActive.Contains(pindexIter)) { + // Add to the list of blocks to remove + vBlocks.emplace_back(pindexIter); + if (pindexIter == pindexBestInvalid) { + // Reset invalid block marker if it was pointing to this block + pindexBestInvalid = nullptr; + } + // Reduce validity + pindexIter->nStatus = std::min(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; + // Update indices + setBlockIndexCandidates.erase(pindexIter); + auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); + while (ret.first != ret.second) { + if (ret.first->second == pindexIter) { + ret.first = mapBlocksUnlinked.erase(ret.first); + } else { + ++ret.first; + } + } + } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) { + setBlockIndexCandidates.insert(pindexIter); + } + } + + // Set pindexBestHeader to the current chain tip + // (since we are about to delete the block it is pointing to) + pindexBestHeader = chainActive.Tip(); + + // Erase block indices on-disk + if (!pblocktree->EraseBatchSync(vBlocks)) { + return AbortNode(state, "Failed to erase from block index database"); + } + + // Erase block indices in-memory + for (auto pindex : vBlocks) { + auto ret = mapBlockIndex.find(*pindex->phashBlock); + if (ret != mapBlockIndex.end()) { + mapBlockIndex.erase(ret); + delete pindex; + } + } + + CheckBlockIndex(); + + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { + return false; + } + + return true; +} + + /** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */ static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { diff --git a/src/validation.h b/src/validation.h index fd40e1d324d42a..a2eba7661d90bd 100644 --- a/src/validation.h +++ b/src/validation.h @@ -338,6 +338,9 @@ class CVerifyDB bool VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth); }; +/** Rewind chain up to the last checkpoint */ +bool RewindBlockIndexToLastCheckpoint(const CChainParams& chainparams, bool& clearWitnessCaches, bool fJustCheck); + /** Replay blocks that aren't fully applied to the database. */ bool ReplayBlocks(const CChainParams& params, CCoinsView* view); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index c47966d5b9b65e..59aff7cd08ee21 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -205,7 +205,7 @@ bool WalletVerify() return true; } -bool InitLoadWallet() +bool InitLoadWallet(bool clearWitnessCaches) { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -214,7 +214,7 @@ bool InitLoadWallet() for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { // create/load wallet - CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir())); + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()), clearWitnessCaches); if (!pwallet) { return false; } diff --git a/src/wallet/init.h b/src/wallet/init.h index 3d0ba7c6b9f33c..3a5c473f33305d 100644 --- a/src/wallet/init.h +++ b/src/wallet/init.h @@ -21,6 +21,6 @@ bool WalletParameterInteraction(); bool WalletVerify(); //! Load wallet databases. -bool InitLoadWallet(); +bool InitLoadWallet(bool clearWitnessCaches); #endif // PIVX_WALLET_INIT_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b58df803c8fc30..d1e1fa9b6e6abe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4157,14 +4157,14 @@ void CWallet::LockIfMyCollateral(const CTransactionRef& ptx) } } -CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path) +CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, bool clearWitnessCaches) { const std::string& walletFile = name; // needed to restore wallet transaction meta data after -zapwallettxes std::vector vWtx; - if (gArgs.GetBoolArg("-zapwallettxes", false)) { + if (clearWitnessCaches || gArgs.GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); std::unique_ptr tempWallet = std::make_unique(name, WalletDatabase::Create(path)); @@ -4278,7 +4278,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& LOCK(cs_main); CBlockIndex* pindexRescan = chainActive.Genesis(); - if (gArgs.GetBoolArg("-rescan", false)) { + if (clearWitnessCaches || gArgs.GetBoolArg("-rescan", false)) { // clear note witness cache before a full rescan walletInstance->ClearNoteWitnessCache(); } else { @@ -4326,7 +4326,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& walletInstance->database->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 - if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2") { + if ((clearWitnessCaches || gArgs.GetBoolArg("-zapwallettxes", false)) && gArgs.GetArg("-zapwallettxes", "1") != "2") { WalletBatch batch(*walletInstance->database); for (const CWalletTx& wtxOld : vWtx) { uint256 hash = wtxOld.GetHash(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a1976db582fd27..99283bec9c8f17 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1189,7 +1189,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool AbandonTransaction(const uint256& hashTx); /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ - static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path); + static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path, bool clearWitnessCaches); /** * Wallet post-init setup