From ce506bc6038d376bb47efd3abad54e8625684e82 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 20 Nov 2025 13:58:22 +0300 Subject: [PATCH 1/2] perf: reduce cs_main lock scope in evodb verify/repair operations Previously, evodb_verify_or_repair_impl held cs_main for the entire operation, which could take minutes when verifying/repairing large block ranges. This caused significant lock contention and blocked other operations requiring cs_main. This commit reduces the cs_main lock scope to only the initial setup phase where we resolve block indexes from the active chain. The actual verification and repair work (applying diffs, rebuilding lists from blocks, verifying snapshots) now runs without holding cs_main. Changes: - Wrap block index resolution in a scoped cs_main lock - Remove AssertLockHeld(cs_main) from helper functions: * RecalculateAndRepairDiffs * CollectSnapshotBlocks * VerifySnapshotPair * RepairSnapshotPair * RebuildListFromBlock (CSpecialTxProcessor) - Update function signatures to remove EXCLUSIVE_LOCKS_REQUIRED(cs_main) This is safe because: - CBlockIndex pointers remain valid after lock release (never deleted) - Block parent relationships (pprev, GetAncestor) are immutable - ReadBlockFromDisk takes cs_main internally when accessing nFile/nDataPos - Helper functions only process already-loaded block data and snapshots - ChainLocks prevent deep reorgs in Dash anyway --- src/evo/deterministicmns.cpp | 8 ----- src/evo/deterministicmns.h | 10 +++---- src/evo/specialtxman.cpp | 2 -- src/evo/specialtxman.h | 3 +- src/rpc/evo.cpp | 58 ++++++++++++++++++------------------ 5 files changed, 34 insertions(+), 47 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 0b79a38e9521..b29622903c59 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1586,8 +1586,6 @@ CDeterministicMNManager::RecalcDiffsResult CDeterministicMNManager::RecalculateA const CBlockIndex* start_index, const CBlockIndex* stop_index, ChainstateManager& chainman, BuildListFromBlockFunc build_list_func, bool repair) { - AssertLockHeld(::cs_main); - RecalcDiffsResult result; result.start_height = start_index->nHeight; result.stop_height = stop_index->nHeight; @@ -1696,8 +1694,6 @@ CDeterministicMNManager::RecalcDiffsResult CDeterministicMNManager::RecalculateA std::vector CDeterministicMNManager::CollectSnapshotBlocks( const CBlockIndex* start_index, const CBlockIndex* stop_index, const Consensus::Params& consensus_params) { - AssertLockHeld(::cs_main); - std::vector snapshot_blocks; // Add the starting snapshot (find the snapshot at or before start) @@ -1749,8 +1745,6 @@ bool CDeterministicMNManager::VerifySnapshotPair( const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, const CDeterministicMNList& to_snapshot, RecalcDiffsResult& result) { - AssertLockHeld(::cs_main); - // Verify this snapshot pair by applying all stored diffs sequentially CDeterministicMNList test_list = from_snapshot; @@ -1795,8 +1789,6 @@ std::vector> CDeterministicMNManage const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, const CDeterministicMNList& to_snapshot, BuildListFromBlockFunc build_list_func, RecalcDiffsResult& result) { - AssertLockHeld(::cs_main); - CDeterministicMNList current_list = from_snapshot; // Temporary storage for recalculated diffs (one per block in this snapshot interval) std::vector> temp_diffs; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index b01fbd5f8f7e..32888c40e8b9 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -743,7 +743,7 @@ class CDeterministicMNManager [[nodiscard]] RecalcDiffsResult RecalculateAndRepairDiffs( const CBlockIndex* start_index, const CBlockIndex* stop_index, ChainstateManager& chainman, BuildListFromBlockFunc build_list_func, - bool repair) EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); + bool repair) EXCLUSIVE_LOCKS_REQUIRED(!cs); // Migration support for nVersion-first CDeterministicMNStateDiff format [[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); @@ -755,14 +755,12 @@ class CDeterministicMNManager // Helper methods for RecalculateAndRepairDiffs std::vector CollectSnapshotBlocks(const CBlockIndex* start_index, const CBlockIndex* stop_index, - const Consensus::Params& consensus_params) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + const Consensus::Params& consensus_params); bool VerifySnapshotPair(const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, - const CDeterministicMNList& to_snapshot, RecalcDiffsResult& result) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + const CDeterministicMNList& to_snapshot, RecalcDiffsResult& result); std::vector> RepairSnapshotPair( const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, - const CDeterministicMNList& to_snapshot, BuildListFromBlockFunc build_list_func, RecalcDiffsResult& result) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + const CDeterministicMNList& to_snapshot, BuildListFromBlockFunc build_list_func, RecalcDiffsResult& result); void WriteRepairedDiffs(const std::vector>& recalculated_diffs, RecalcDiffsResult& result) EXCLUSIVE_LOCKS_REQUIRED(!cs); }; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index a788bc22437f..f5bd3f810117 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -185,8 +185,6 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul bool debugLogs, BlockValidationState& state, CDeterministicMNList& mnListRet) { - AssertLockHeld(cs_main); - // Verify that prevList either represents an empty/initial state (default-constructed), // or it matches the previous block's hash. assert(prevList == CDeterministicMNList() || prevList.GetBlockHash() == pindexPrev->GetBlockHash()); diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index 461fa8d222c1..9faa57dd02f4 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -83,8 +83,7 @@ class CSpecialTxProcessor // Used for rebuilding diffs from trusted snapshots bool RebuildListFromBlock(const CBlock& block, gsl::not_null pindexPrev, const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, - BlockValidationState& state, CDeterministicMNList& mnListRet) - EXCLUSIVE_LOCKS_REQUIRED(cs_main); + BlockValidationState& state, CDeterministicMNList& mnListRet); private: bool CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx, diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 12bd74cc79dd..7de013d32728 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1760,37 +1760,38 @@ static UniValue evodb_verify_or_repair_impl(const JSONRPCRequest& request, bool CDeterministicMNManager& dmnman = *CHECK_NONFATAL(node.dmnman); CChainstateHelper& chain_helper = *CHECK_NONFATAL(node.chain_helper); - LOCK(::cs_main); - const CBlockIndex* start_index; const CBlockIndex* stop_index; - // Default to DIP0003 activation height if startBlock not specified - if (request.params[0].isNull()) { - const auto& consensus_params = Params().GetConsensus(); - start_index = chainman.ActiveChain()[consensus_params.DIP0003Height]; - if (!start_index) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot find DIP0003 activation block"); - } - } else { - uint256 start_block_hash = ParseBlock(request.params[0], chainman, "startBlock"); - start_index = chainman.m_blockman.LookupBlockIndex(start_block_hash); - if (!start_index) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Start block not found"); + { + LOCK(::cs_main); + // Default to DIP0003 activation height if startBlock not specified + if (request.params[0].isNull()) { + const auto& consensus_params = Params().GetConsensus(); + start_index = chainman.ActiveChain()[consensus_params.DIP0003Height]; + if (!start_index) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot find DIP0003 activation block"); + } + } else { + uint256 start_block_hash = ParseBlock(request.params[0], chainman, "startBlock"); + start_index = chainman.m_blockman.LookupBlockIndex(start_block_hash); + if (!start_index) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Start block not found"); + } } - } - // Default to chain tip if stopBlock not specified - if (request.params[1].isNull()) { - stop_index = chainman.ActiveChain().Tip(); - if (!stop_index) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot find chain tip"); - } - } else { - uint256 stop_block_hash = ParseBlock(request.params[1], chainman, "stopBlock"); - stop_index = chainman.m_blockman.LookupBlockIndex(stop_block_hash); - if (!stop_index) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Stop block not found"); + // Default to chain tip if stopBlock not specified + if (request.params[1].isNull()) { + stop_index = chainman.ActiveChain().Tip(); + if (!stop_index) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot find chain tip"); + } + } else { + uint256 stop_block_hash = ParseBlock(request.params[1], chainman, "stopBlock"); + stop_index = chainman.m_blockman.LookupBlockIndex(stop_block_hash); + if (!stop_index) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Stop block not found"); + } } } @@ -1802,12 +1803,11 @@ static UniValue evodb_verify_or_repair_impl(const JSONRPCRequest& request, bool throw JSONRPCError(RPC_INVALID_PARAMETER, "stopBlock must be >= startBlock"); } - // Create a callback that wraps CSpecialTxProcessor::BuildNewListFromBlock - // NO_THREAD_SAFETY_ANALYSIS: cs_main is held by the calling function (evodb_verify_or_repair_impl) + // Create a callback that wraps CSpecialTxProcessor::RebuildListFromBlock auto build_list_func = [&chain_helper](const CBlock& block, gsl::not_null pindexPrev, const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, BlockValidationState& state, - CDeterministicMNList& mnListRet) NO_THREAD_SAFETY_ANALYSIS -> bool { + CDeterministicMNList& mnListRet) -> bool { return chain_helper.special_tx->RebuildListFromBlock(block, pindexPrev, prevList, view, debugLogs, state, mnListRet); }; From 084bb62307824e177ffdd3db401c5161a4dcebc2 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 20 Nov 2025 15:13:21 +0300 Subject: [PATCH 2/2] chore: clang-format --- src/evo/deterministicmns.h | 15 ++++++++------- src/evo/specialtxman.h | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 32888c40e8b9..80aabbe113aa 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -740,10 +740,10 @@ class CDeterministicMNManager BlockValidationState& state, CDeterministicMNList& mnListRet)>; - [[nodiscard]] RecalcDiffsResult RecalculateAndRepairDiffs( - const CBlockIndex* start_index, const CBlockIndex* stop_index, - ChainstateManager& chainman, BuildListFromBlockFunc build_list_func, - bool repair) EXCLUSIVE_LOCKS_REQUIRED(!cs); + [[nodiscard]] RecalcDiffsResult RecalculateAndRepairDiffs(const CBlockIndex* start_index, + const CBlockIndex* stop_index, ChainstateManager& chainman, + BuildListFromBlockFunc build_list_func, bool repair) + EXCLUSIVE_LOCKS_REQUIRED(!cs); // Migration support for nVersion-first CDeterministicMNStateDiff format [[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); @@ -755,9 +755,10 @@ class CDeterministicMNManager // Helper methods for RecalculateAndRepairDiffs std::vector CollectSnapshotBlocks(const CBlockIndex* start_index, const CBlockIndex* stop_index, - const Consensus::Params& consensus_params); - bool VerifySnapshotPair(const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, - const CDeterministicMNList& to_snapshot, RecalcDiffsResult& result); + const Consensus::Params& consensus_params); + bool VerifySnapshotPair(const CBlockIndex* from_index, const CBlockIndex* to_index, + const CDeterministicMNList& from_snapshot, const CDeterministicMNList& to_snapshot, + RecalcDiffsResult& result); std::vector> RepairSnapshotPair( const CBlockIndex* from_index, const CBlockIndex* to_index, const CDeterministicMNList& from_snapshot, const CDeterministicMNList& to_snapshot, BuildListFromBlockFunc build_list_func, RecalcDiffsResult& result); diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index 9faa57dd02f4..de293d0dfaba 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -82,8 +82,8 @@ class CSpecialTxProcessor // Variant that takes an explicit starting list instead of loading from GetListForBlock // Used for rebuilding diffs from trusted snapshots bool RebuildListFromBlock(const CBlock& block, gsl::not_null pindexPrev, - const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, - BlockValidationState& state, CDeterministicMNList& mnListRet); + const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, + BlockValidationState& state, CDeterministicMNList& mnListRet); private: bool CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx,