Skip to content

Commit 2e5fdd8

Browse files
UdjinM6claude
andcommitted
feat: verify and repair evodb diffs automatically at node startup
Automatically verify and repair deterministic masternode list diffs in evodb during node startup. Helps detect and fix database corruption without manual intervention. - Add DB_LIST_REPAIRED marker to track when repair is complete - Skip repair during reindex (fresh rebuild) - Add -forceevodbrepair flag to force re-verification - Shutdown gracefully on critical errors with user instructions - Run in background thread with minimal cs_main locking Co-Authored-By: Claude Code (Anthropic) <[email protected]>
1 parent c9faf42 commit 2e5fdd8

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

src/evo/deterministicmns.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
static const std::string DB_LIST_SNAPSHOT = "dmn_S3";
3333
static const std::string DB_LIST_DIFF = "dmn_D4"; // Bumped for nVersion-first format
3434
static const std::string DB_LIST_DIFF_LEGACY = "dmn_D3"; // Legacy format key
35+
static const std::string DB_LIST_REPAIRED = "dmn_R1";
3536

3637
uint64_t CDeterministicMN::GetInternalId() const
3738
{
@@ -1690,6 +1691,20 @@ CDeterministicMNManager::RecalcDiffsResult CDeterministicMNManager::RecalculateA
16901691
return result;
16911692
}
16921693

1694+
bool CDeterministicMNManager::IsRepaired() const { return m_evoDb.Exists(DB_LIST_REPAIRED); }
1695+
1696+
void CDeterministicMNManager::CompleteRepair()
1697+
{
1698+
auto dbTx = m_evoDb.BeginTransaction();
1699+
m_evoDb.Write(DB_LIST_REPAIRED, 1);
1700+
dbTx->Commit();
1701+
// flush it to disk
1702+
if (!m_evoDb.CommitRootTransaction()) {
1703+
LogPrintf("CDeterministicMNManager::%s -- Failed to commit to evoDB\n", __func__);
1704+
assert(false);
1705+
}
1706+
}
1707+
16931708
std::vector<const CBlockIndex*> CDeterministicMNManager::CollectSnapshotBlocks(
16941709
const CBlockIndex* start_index, const CBlockIndex* stop_index, const Consensus::Params& consensus_params)
16951710
{

src/evo/deterministicmns.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,8 @@ class CDeterministicMNManager
744744
const CBlockIndex* stop_index, ChainstateManager& chainman,
745745
BuildListFromBlockFunc build_list_func, bool repair)
746746
EXCLUSIVE_LOCKS_REQUIRED(!cs);
747+
[[nodiscard]] bool IsRepaired() const;
748+
void CompleteRepair();
747749

748750
// Migration support for nVersion-first CDeterministicMNStateDiff format
749751
[[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);

src/init.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@
8181
#include <coinjoin/server.h>
8282
#include <coinjoin/walletman.h>
8383
#include <dsnotificationinterface.h>
84+
#include <evo/chainhelper.h>
8485
#include <evo/deterministicmns.h>
8586
#include <evo/evodb.h>
8687
#include <evo/mnhftx.h>
88+
#include <evo/specialtxman.h>
8789
#include <flat-database.h>
8890
#include <governance/governance.h>
8991
#include <instantsend/instantsend.h>
@@ -751,6 +753,7 @@ void SetupServerArgs(ArgsManager& argsman)
751753
argsman.AddArg("-checkmempool=<n>", strprintf("Run mempool consistency checks every <n> transactions. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
752754
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);
753755
argsman.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
756+
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);
754757
argsman.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
755758
argsman.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
756759
argsman.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> 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)
24082411
LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
24092412
}
24102413

2414+
if (fReindex || fReindexChainState) {
2415+
LogPrintf("Skipping evodb repair during reindex\n");
2416+
node.dmnman->CompleteRepair(); // Mark as repaired since we're rebuilding fresh
2417+
} else if (node.dmnman->IsRepaired() && !args.GetBoolArg("-forceevodbrepair", false)) {
2418+
LogPrintf("Masternode list diffs are already repaired\n");
2419+
} else {
2420+
const CBlockIndex* start_index;
2421+
const CBlockIndex* stop_index;
2422+
{
2423+
LOCK(cs_main);
2424+
const auto& consensus_params = Params().GetConsensus();
2425+
start_index = chainman.ActiveChain()[consensus_params.DIP0003Height];
2426+
stop_index = chainman.ActiveChain().Tip();
2427+
}
2428+
2429+
if (start_index && stop_index && start_index->nHeight < stop_index->nHeight) {
2430+
LogPrintf("Verifying and repairing masternode list diffs...\n");
2431+
const auto start{SteadyClock::now()};
2432+
// Create a callback that wraps CSpecialTxProcessor::BuildNewListFromBlock
2433+
auto build_list_func = [&node](const CBlock& block, gsl::not_null<const CBlockIndex*> pindexPrev,
2434+
const CDeterministicMNList& prevList, const CCoinsViewCache& view,
2435+
bool debugLogs, BlockValidationState& state,
2436+
CDeterministicMNList& mnListRet) -> bool {
2437+
return node.chain_helper->special_tx->RebuildListFromBlock(block, pindexPrev, prevList, view, debugLogs, state, mnListRet);
2438+
};
2439+
auto result = node.dmnman->RecalculateAndRepairDiffs(start_index, stop_index, chainman, build_list_func, true);
2440+
2441+
if (!result.verification_errors.empty()) {
2442+
LogPrintf("WARNING: Verification errors:\n%s\n", Join(result.verification_errors, "\n"));
2443+
}
2444+
2445+
if (!result.repair_errors.empty()) {
2446+
// Critical errors occurred - reindex required
2447+
LogPrintf("Failed to repair masternode list diffs. Database corruption detected. " /* Continued */
2448+
"Please restart with -reindex to rebuild the database.\n"
2449+
"Errors:\n%s\n",
2450+
Join(result.repair_errors, "\n"));
2451+
StartShutdown();
2452+
return;
2453+
}
2454+
node.dmnman->CompleteRepair();
2455+
LogPrintf("Successfully repaired %d masternode list diffs, verified %d snapshots in %ds\n",
2456+
result.diffs_recalculated, result.snapshots_verified,
2457+
Ticks<std::chrono::seconds>(SteadyClock::now() - start));
2458+
}
2459+
}
2460+
24112461
if (node.mn_activeman != nullptr) {
24122462
node.mn_activeman->Init(chainman.ActiveTip());
24132463
}

0 commit comments

Comments
 (0)