diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index cca3bb82b2b0..0fbf6fb7ba0e 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -32,6 +32,7 @@ static const std::string DB_LIST_SNAPSHOT = "dmn_S3"; static const std::string DB_LIST_DIFF = "dmn_D4"; // Bumped for nVersion-first format static const std::string DB_LIST_DIFF_LEGACY = "dmn_D3"; // Legacy format key +static const std::string DB_LIST_REPAIRED = "dmn_R1"; uint64_t CDeterministicMN::GetInternalId() const { @@ -1690,6 +1691,20 @@ CDeterministicMNManager::RecalcDiffsResult CDeterministicMNManager::RecalculateA return result; } +bool CDeterministicMNManager::IsRepaired() const { return m_evoDb.Exists(DB_LIST_REPAIRED); } + +void CDeterministicMNManager::CompleteRepair() +{ + auto dbTx = m_evoDb.BeginTransaction(); + m_evoDb.Write(DB_LIST_REPAIRED, 1); + dbTx->Commit(); + // flush it to disk + if (!m_evoDb.CommitRootTransaction()) { + LogPrintf("CDeterministicMNManager::%s -- Failed to commit to evoDB\n", __func__); + assert(false); + } +} + std::vector CDeterministicMNManager::CollectSnapshotBlocks( const CBlockIndex* start_index, const CBlockIndex* stop_index, const Consensus::Params& consensus_params) { diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 80aabbe113aa..8ba7e9ab02cb 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -744,6 +744,8 @@ class CDeterministicMNManager const CBlockIndex* stop_index, ChainstateManager& chainman, BuildListFromBlockFunc build_list_func, bool repair) EXCLUSIVE_LOCKS_REQUIRED(!cs); + [[nodiscard]] bool IsRepaired() const; + void CompleteRepair(); // Migration support for nVersion-first CDeterministicMNStateDiff format [[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); diff --git a/src/init.cpp b/src/init.cpp index 642df953f34f..501688cb0cb2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -81,9 +81,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -751,6 +753,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-checkmempool=", strprintf("Run mempool consistency checks every transactions. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-deprecatedrpc=", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-forceevodbrepair", "Force evodb masternode list diff verification and repair on startup, even if already repaired (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-limitancestorcount=", strprintf("Do not accept transactions if number of in-mempool ancestors is or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-limitancestorsize=", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-limitdescendantcount=", strprintf("Do not accept transactions if any ancestor would have or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); @@ -2408,6 +2411,53 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", Ticks(SteadyClock::now() - start)); } + if (fReindex || fReindexChainState) { + LogPrintf("Skipping evodb repair during reindex\n"); + node.dmnman->CompleteRepair(); // Mark as repaired since we're rebuilding fresh + } else if (node.dmnman->IsRepaired() && !args.GetBoolArg("-forceevodbrepair", false)) { + LogPrintf("Masternode list diffs are already repaired\n"); + } else { + const CBlockIndex* start_index; + const CBlockIndex* stop_index; + { + LOCK(cs_main); + const auto& consensus_params = Params().GetConsensus(); + start_index = chainman.ActiveChain()[consensus_params.DIP0003Height]; + stop_index = chainman.ActiveChain().Tip(); + } + + if (start_index && stop_index && start_index->nHeight < stop_index->nHeight) { + LogPrintf("Verifying and repairing masternode list diffs...\n"); + const auto start{SteadyClock::now()}; + // Create a callback that wraps CSpecialTxProcessor::BuildNewListFromBlock + auto build_list_func = [&node](const CBlock& block, gsl::not_null pindexPrev, + const CDeterministicMNList& prevList, const CCoinsViewCache& view, + bool debugLogs, BlockValidationState& state, + CDeterministicMNList& mnListRet) -> bool { + return node.chain_helper->special_tx->RebuildListFromBlock(block, pindexPrev, prevList, view, debugLogs, state, mnListRet); + }; + auto result = node.dmnman->RecalculateAndRepairDiffs(start_index, stop_index, chainman, build_list_func, true); + + if (!result.verification_errors.empty()) { + LogPrintf("WARNING: Verification errors:\n%s\n", Join(result.verification_errors, "\n")); + } + + if (!result.repair_errors.empty()) { + // Critical errors occurred - reindex required + LogPrintf("Failed to repair masternode list diffs. Database corruption detected. " /* Continued */ + "Please restart with -reindex to rebuild the database.\n" + "Errors:\n%s\n", + Join(result.repair_errors, "\n")); + StartShutdown(); + return; + } + node.dmnman->CompleteRepair(); + LogPrintf("Successfully repaired %d masternode list diffs, verified %d snapshots in %ds\n", + result.diffs_recalculated, result.snapshots_verified, + Ticks(SteadyClock::now() - start)); + } + } + if (node.mn_activeman != nullptr) { node.mn_activeman->Init(chainman.ActiveTip()); }