-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor: drop mutex and atomic from CMasternodeMetaInfo, access to object directly without shared_ptr #6894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
…o a private member
It is already guarded by CMasternodeMetaMan::cs
WalkthroughThe PR refactors masternode metadata into a value-based CMasternodeMetaInfo stored and managed by CMasternodeMetaMan. It replaces many direct per-entry pointer accesses (GetMetaInfo(...)->...) with new manager-level APIs (IsDsqOver, Allow/DisallowMixing, IsValidForMixingTxes, Set/GetLastOutboundAttempt/Success, OutboundFailedTooManyTimes, AddGovernanceVote, Set/Reset/GetPlatformBan, etc.). Call sites across coinjoin, evo (deterministicmns, mnauth), llmq, governance, net, net_processing, rpc, and related files are updated to use the new interfaces; DSQ, mixing, outbound, governance vote, and platform-ban logic are centralized and simplified. Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/net_processing.cpp (1)
3480-3485
: Still need the mixing meta lock
IsValidForMixingTxes
assumesCMasternodeMetaMan::cs
is held; the previous code performed the meta check inside the manager’s critical section. We call it here without locking, so the method’s internalAssertLockHeld
will trip (or worse, we race). AcquireWITH_LOCK(mn_metaman.GetCs(), return mn_metaman.IsValidForMixingTxes(...))
(or similar helper) before invoking the new API.
🧹 Nitpick comments (5)
src/masternode/meta.h (4)
56-61
: Serialization format change: confirm backward compatibility and handle duplicatesYou now serialize a vector of CMasternnodeMetaInfo by value. Ensure this matches prior on-disk format for the same SERIALIZATION_VERSION_STRING. Also, if the vector contains duplicate m_protx_hash entries, later ones are silently ignored by emplace.
Consider using insert_or_assign to make last-wins explicit.
Apply this small change to make behavior explicit:
- for (auto& mm : tmpMetaInfo) { - metaInfos.emplace(mm.m_protx_hash, CMasternodeMetaInfo{std::move(mm)}); - } + for (auto& mm : tmpMetaInfo) { + metaInfos.insert_or_assign(mm.m_protx_hash, std::move(mm)); + }
71-73
: Add thread-safety annotation to outbound settersSetLastOutboundAttempt/Success mutate shared state. For consistency with the new locking model, add the same EXCLUSIVE_LOCKS_REQUIRED(!cs) (or appropriate annotation) used on other mutators.
39-41
: Consider guarding against attempt counter overflowoutboundAttemptCount is int and is unbounded; in pathological cases it could overflow. Either use a wider unsigned type or saturate at a reasonable max in SetLastOutboundAttempt.
Example:
- void SetLastOutboundAttempt(int64_t t) { lastOutboundAttempt = t; ++outboundAttemptCount; } + void SetLastOutboundAttempt(int64_t t) { + lastOutboundAttempt = t; + if (outboundAttemptCount < std::numeric_limits<int>::max()) ++outboundAttemptCount; + }
92-106
: Serialize/Unserialize: minor efficiency/readability nits
- Reserve tmpMetaInfo to avoid reallocations (metaInfos.size()).
- Use insert_or_assign during Unserialize as suggested above.
- std::vector<CMasternodeMetaInfo> tmpMetaInfo; - for (const auto& p : metaInfos) { - tmpMetaInfo.emplace_back(p.second); - } + std::vector<CMasternnodeMetaInfo> tmpMetaInfo; + tmpMetaInfo.reserve(metaInfos.size()); + for (const auto& [_, mi] : metaInfos) { + tmpMetaInfo.emplace_back(mi); + }Also applies to: 114-125
src/governance/object.cpp (1)
151-152
: Behavioral change: dropped failure handling from AddGovernanceVotePreviously, failure from AddGovernanceVote aborted processing. The new API is void; processing now proceeds unconditionally. Confirm this is intentional and that rate/duplicate checks are fully covered earlier (fileVotes/rate checks).
If you still need a guard, consider having AddGovernanceVote return a bool (no-op vs. insert) and log on false.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/coinjoin/client.cpp
(2 hunks)src/coinjoin/server.cpp
(2 hunks)src/evo/deterministicmns.cpp
(1 hunks)src/evo/mnauth.cpp
(1 hunks)src/governance/object.cpp
(1 hunks)src/llmq/dkgsession.cpp
(1 hunks)src/llmq/utils.cpp
(1 hunks)src/masternode/meta.cpp
(4 hunks)src/masternode/meta.h
(5 hunks)src/net.cpp
(4 hunks)src/net_processing.cpp
(2 hunks)src/rpc/evo.cpp
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}
: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/llmq/dkgsession.cpp
src/llmq/utils.cpp
src/net_processing.cpp
src/net.cpp
src/governance/object.cpp
src/coinjoin/server.cpp
src/coinjoin/client.cpp
src/rpc/evo.cpp
src/evo/deterministicmns.cpp
src/evo/mnauth.cpp
src/masternode/meta.h
src/masternode/meta.cpp
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Masternode lists must use immutable data structures (Immer library) for thread safety
Files:
src/evo/deterministicmns.cpp
src/evo/mnauth.cpp
src/masternode/meta.h
src/masternode/meta.cpp
🔇 Additional comments (13)
src/llmq/utils.cpp (1)
915-916
: Good API migrationSwitched to mn_metaman.GetLastOutboundSuccess(...) keeps logic unchanged and centralizes access.
src/rpc/evo.cpp (1)
1382-1383
: Avoids shared_ptr/meta indirectionUsing GetInfo(...).ToJson() is simpler and matches value-based storage. Minor perf note: this copies the meta before JSON, which is fine here.
src/llmq/dkgsession.cpp (1)
495-502
: Correctly moved to manager-level checksOutboundFailedTooManyTimes/IsPlatformBanned calls on m_mn_metaman are consistent with the refactor and preserve behavior.
src/coinjoin/server.cpp (2)
81-91
: Centralized DSQ gating via IsDsqOverGood consolidation; logs and ERR_RECENT behavior preserved.
173-179
: Consistent DSQ abuse preventionUsing IsDsqOver(...) with GetValidMNsCount() matches the new API and intent to avoid domination.
src/masternode/meta.h (1)
200-206
: Public API surface looks coherentThe value-returning GetInfo and centralized IsDsqOver/readers align with the new locking model and reduce unsafe exposure.
Optionally, verify no old pointer-style usages remain:
src/coinjoin/client.cpp (2)
119-124
: Centralized DSQ rate-limit — looks good; confirm lockingUsing m_mn_metaman.IsDsqOver(...) simplifies and unifies the guard. Please confirm these new manager-level APIs perform internal locking of CMasternodeMetaMan::cs (since CMasternodeMetaInfo dropped its own mutex/atomic) so call sites don’t need to hold the lock explicitly.
1166-1169
: Unified check in StartNewQueue — good; verify count semanticsThe IsDsqOver(dmn->proTxHash, nMnCount) gate is consistent with ProcessMessage. Please double-check IsDsqOver expects non-weighted MN count (GetValidMNsCount), not weighted, to avoid threshold drift.
src/net.cpp (5)
3445-3449
: Swap to GetLastOutboundAttempt — verify time units and lockingnANow is seconds (GetTimestd::chrono::seconds().count()). Please confirm GetLastOutboundAttempt returns the same unit and that internal locking of CMasternodeMetaMan::cs is handled inside the method.
3469-3471
: SetLastOutboundSuccess on existing outbound — ensure backoff resetThe success mark here is appropriate. Confirm it also clears/decays failure/backoff state for this MN so future attempts aren’t unnecessarily delayed.
3476-3482
: Second use of GetLastOutboundAttempt — same unit/lock checkSame ask as above: confirm return unit matches nANow and that manager-level methods encapsulate required locking.
3527-3528
: SetLastOutboundAttempt before dialing — semantics checkMarking the attempt pre-connection is fine for backoff. Please verify failure paths always advance failure counters (or similar) so OutboundFailedTooManyTimes reflects this attempt if connect fails immediately.
3541-3543
: OutboundFailedTooManyTimes usage — confirm increment pathEnsure the code path that led here increments the failure count used by OutboundFailedTooManyTimes() within CMasternodeMetaMan, and that concurrency with list updates is internally synchronized.
if (m_mn_metaman.ResetPlatformBan(opt_proTx->proTxHash, nHeight)) { | ||
LogPrint(BCLog::LLMQ, "%s -- MN %s is failed to Platform revived at height %d\n", __func__, | ||
opt_proTx->proTxHash.ToString(), nHeight); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify the log message for platform ban revival.
The log message "is failed to Platform revived" is grammatically incorrect and confusing. Since this block now executes when the revival succeeds (not when it fails, as in the previous implementation), consider revising the message to something clearer like:
LogPrint(BCLog::LLMQ, "%s -- MN %s platform ban revived at height %d\n", __func__,
opt_proTx->proTxHash.ToString(), nHeight);
🤖 Prompt for AI Agents
In src/evo/deterministicmns.cpp around lines 671-674, the LogPrint message is
grammatically incorrect and misleading ("is failed to Platform revived") and
should reflect that ResetPlatformBan succeeded; update the log string to a
clear, concise message such as "MN %s platform ban revived at height %d"
(preserve __func__ and the proTxHash.ToString() and nHeight parameters) so the
log accurately represents a successful revival.
if (!peer.IsInboundConn()) { | ||
mn_metaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetTime<std::chrono::seconds>().count()); | ||
mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash, GetTime<std::chrono::seconds>().count()); | ||
if (peer.m_masternode_probe_connection) { | ||
LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard SetLastOutboundSuccess with mn meta lock
SetLastOutboundSuccess
requires the manager mutex to be held (the old code locked via GetMetaInfo
). We’re now calling it bare, which violates the contract and will assert. Wrap the call in the manager’s lock (e.g. WITH_LOCK(mn_metaman.GetCs(), mn_metaman.SetLastOutboundSuccess(...));
).
🤖 Prompt for AI Agents
In src/evo/mnauth.cpp around lines 105 to 108, SetLastOutboundSuccess is called
without holding mn_metaman's mutex which violates the manager's locking contract
and will assert; wrap the call with the manager lock, e.g. use
WITH_LOCK(mn_metaman.GetCs(),
mn_metaman.SetLastOutboundSuccess(mnauth.proRegTxHash,
GetTime<std::chrono::seconds>().count())); and keep the rest of the logic
unchanged so the call is protected by the manager's mutex.
bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const | ||
{ | ||
auto metaInfo = GetMetaInfo(proTxHash); | ||
if (metaInfo == nullptr) { | ||
// return a threshold which is slightly above nDsqCount i.e. a no-go | ||
return nDsqCount + 1; | ||
LOCK(cs); | ||
auto it = metaInfos.find(protx_hash); | ||
if (it != metaInfos.end()) { | ||
LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); | ||
return false; | ||
} | ||
return metaInfo->GetLastDsq() + nMnCount / 5; | ||
const auto& meta_info = it->second; | ||
int64_t last_dsq = meta_info.m_last_dsq; | ||
int64_t threshold = last_dsq + mn_count / 5; | ||
|
||
LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", | ||
protx_hash.ToString(), last_dsq, threshold, nDsqCount); | ||
return last_dsq != 0 && threshold > nDsqCount; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the inverted logic in IsDsqOver.
There is a critical logic error in this method. Line 93 checks if (it != metaInfos.end())
and returns false
when the entry is found. However, line 97 then attempts to access it->second
, which would be invalid if the iterator points to end()
(i.e., when the entry is not found).
The condition on line 93 should be inverted to check for the "not found" case:
Apply this diff to fix the logic error:
bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const
{
LOCK(cs);
auto it = metaInfos.find(protx_hash);
- if (it != metaInfos.end()) {
+ if (it == metaInfos.end()) {
LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString());
return false;
}
const auto& meta_info = it->second;
int64_t last_dsq = meta_info.m_last_dsq;
int64_t threshold = last_dsq + mn_count / 5;
LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n",
protx_hash.ToString(), last_dsq, threshold, nDsqCount);
return last_dsq != 0 && threshold > nDsqCount;
}
Additionally, the log message on line 94 appears incorrect. It says "node %s is logged" when the node is not found. Consider updating it to: "DSQUEUE -- node %s not found in metaInfos\n".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const | |
{ | |
auto metaInfo = GetMetaInfo(proTxHash); | |
if (metaInfo == nullptr) { | |
// return a threshold which is slightly above nDsqCount i.e. a no-go | |
return nDsqCount + 1; | |
LOCK(cs); | |
auto it = metaInfos.find(protx_hash); | |
if (it != metaInfos.end()) { | |
LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); | |
return false; | |
} | |
return metaInfo->GetLastDsq() + nMnCount / 5; | |
const auto& meta_info = it->second; | |
int64_t last_dsq = meta_info.m_last_dsq; | |
int64_t threshold = last_dsq + mn_count / 5; | |
LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", | |
protx_hash.ToString(), last_dsq, threshold, nDsqCount); | |
return last_dsq != 0 && threshold > nDsqCount; | |
} | |
bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const | |
{ | |
LOCK(cs); | |
auto it = metaInfos.find(protx_hash); | |
if (it == metaInfos.end()) { | |
LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); | |
return false; | |
} | |
const auto& meta_info = it->second; | |
int64_t last_dsq = meta_info.m_last_dsq; | |
int64_t threshold = last_dsq + mn_count / 5; | |
LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", | |
protx_hash.ToString(), last_dsq, threshold, nDsqCount); | |
return last_dsq != 0 && threshold > nDsqCount; | |
} |
🤖 Prompt for AI Agents
In src/masternode/meta.cpp around lines 89 to 104, the iterator check is
inverted and leads to invalid access: change the if condition to check for "not
found" (it == metaInfos.end()), log "DSQUEUE -- node %s not found in
metaInfos\n" in that branch and return false there, and otherwise proceed to use
it->second; ensure the rest of the function uses the found meta_info to compute
last_dsq, threshold, and return the correct boolean expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// At this point, the outgoing message serialization version can't change. | ||
const auto meta_info = m_mn_metaman.GetMetaInfo(ban_msg.m_protx_hash); | ||
if (meta_info->SetPlatformBan(true, ban_msg.m_requested_height)) { | ||
LogPrintf("PLATFORMBAN -- forward message to other nodes\n"); | ||
m_mn_metaman.RememberPlatformBan(hash, std::move(ban_msg)); | ||
if (m_mn_metaman.SetPlatformBan(hash, std::move(ban_msg))) { | ||
LogPrintf("PLATFORMBAN -- hash: %s forward message to other nodes\n", hash.ToString()); | ||
ret.m_inventory.emplace_back(MSG_PLATFORM_BAN, hash); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing ban propagation after failed SetPlatformBan
SetPlatformBan
now returns false
when the entry was already present. Previously we always forwarded by calling RememberPlatformBan
regardless. Dropping the relay in this branch breaks propagation of matching quorum signatures (peers stop seeing refreshed bans). Keep forwarding when the manager reports “already had it”—only skip if the call throws/invalidates.
🤖 Prompt for AI Agents
In src/net_processing.cpp around lines 3607 to 3611, the current logic only
forwards the platform-ban message when m_mn_metaman.SetPlatformBan returns true;
change it so that a false return that indicates “already present” still results
in forwarding the ban inventory (i.e., still emplace MSG_PLATFORM_BAN) so
matching quorum signature refreshes propagate — only avoid forwarding if
SetPlatformBan signals an error/invalidation (exception or explicit invalid
status); in practice, treat a normal false (already-existed) the same as true
for relay, and only suppress relay on thrown/invalid cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/masternode/meta.h (1)
54-54
: Copy constructor may be expensive for large governance maps.The defaulted copy constructor will perform a deep copy of
mapGovernanceObjectsVotedOn
, which could be expensive if a masternode has voted on many governance objects. SinceCMasternodeMetaInfo
is now returned by value fromGetInfo()
, this copy cost will be incurred on every call.Consider whether the call sites need a full copy, or if they could work with const references where appropriate. If copies are necessary, document the performance implications or consider optimizing the storage structure if governance maps become large.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/masternode/meta.h
(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}
: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/masternode/meta.h
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Masternode lists must use immutable data structures (Immer library) for thread safety
Files:
src/masternode/meta.h
🔇 Additional comments (6)
src/masternode/meta.h (6)
98-106
: LGTM!The serialization correctly creates a vector from the value-based map entries. The use of
p.second
at line 103 properly extracts theCMasternodeMetaInfo
value from the map.
109-124
: LGTM!The unserialization correctly reconstructs the map using
mm.m_protx_hash
as the key and move semantics for the value. This aligns well with the value-based storage model.
188-190
: LGTM!The private helpers correctly return references (
GetMetaInfo
) and const references (GetMetaInfoOrDefault
) while requiring the lock to be held by the caller. This is an efficient pattern for internal use within the class.
199-199
: LGTM!Returning
CMasternodeMetaInfo
by value ensures thread safety by providing a copy to the caller, eliminating the need for the caller to hold the lock after the method returns. This is the correct approach for the public API.
205-227
: LGTM!The public API methods are consistently annotated with
EXCLUSIVE_LOCKS_REQUIRED(!cs)
, indicating they acquire the lock internally. This provides a clean, thread-safe interface for callers who don't need to manage locking manually.
72-83
: Verify platform ban idempotency at the same height.The logic at lines 77-78 prevents unsetting a ban at the same height, but allows setting a ban multiple times at the same height (when
is_banned == true
). This might allow duplicate processing of the same ban message.Verify whether callers deduplicate ban messages before calling
SetPlatformBan
, or if this method should enforce stricter idempotency:
6834d68
to
cdc9825
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/masternode/meta.cpp (1)
93-108
: Critical logic error in IsDsqOver already flagged in past review.This issue was previously identified: Line 97 checks
if (it != metaInfos.end())
(meaning "found"), but then returns false and does not use the iterator. Subsequently, line 101 attempts to accessit->second
, which is undefined behavior when the iterator is atend()
(not found).The condition must be inverted to check for "not found" before returning false.
Apply this diff to fix the logic error:
bool CMasternodeMetaMan::IsDsqOver(const uint256& protx_hash, int mn_count) const { LOCK(cs); auto it = metaInfos.find(protx_hash); - if (it != metaInfos.end()) { - LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s is logged\n", protx_hash.ToString()); + if (it == metaInfos.end()) { + LogPrint(BCLog::COINJOIN, "DSQUEUE -- node %s not found in metaInfos\n", protx_hash.ToString()); return false; } const auto& meta_info = it->second; int64_t last_dsq = meta_info.m_last_dsq; int64_t threshold = last_dsq + mn_count / 5; LogPrint(BCLog::COINJOIN, "DSQUEUE -- mn: %s last_dsq: %d dsq_threshold: %d nDsqCount: %d\n", protx_hash.ToString(), last_dsq, threshold, nDsqCount); return last_dsq != 0 && threshold > nDsqCount; }
🧹 Nitpick comments (1)
src/masternode/meta.cpp (1)
16-18
: Minor: Consider renaming the default instance for clarity.The variable name
default_meta_info_meta_info
contains redundant repetition. Consider simplifying todefault_meta_info
ordefault_metainfo
.Apply this diff to improve naming:
namespace { -static const CMasternodeMetaInfo default_meta_info_meta_info{}; +static const CMasternodeMetaInfo default_meta_info{}; } // anonymous namespaceThen update line 73:
- if (it == metaInfos.end()) return default_meta_info_meta_info; + if (it == metaInfos.end()) return default_meta_info;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/masternode/meta.cpp
(4 hunks)src/masternode/meta.h
(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}
: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/masternode/meta.cpp
src/masternode/meta.h
src/{masternode,evo}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Masternode lists must use immutable data structures (Immer library) for thread safety
Files:
src/masternode/meta.cpp
src/masternode/meta.h
🔇 Additional comments (13)
src/masternode/meta.cpp (4)
110-118
: Verify atomic increment ordering is correct.
nDsqCount++
(line 112) increments the atomic counter before acquiring the lock. WhilenDsqCount
is atomic and this is safe for the increment itself, confirm that readingnDsqCount.load()
on line 116 after incrementing but potentially from a different thread's perspective won't cause any DSQ sequencing issues.The current pattern appears intentional—incrementing the global counter first, then recording that value under lock—but please verify this ordering satisfies the DSQ queue semantics across concurrent AllowMixing calls.
126-130
: Confirm mixing transaction threshold semantics.Line 129 uses
m_mixing_tx_count <= MASTERNODE_MAX_MIXING_TXES
, allowing up to 5 mixing transactions inclusive. Ensure this matches the intended policy (i.e., 5 is the maximum allowed, not 4).
189-197
: LGTM: ResetPlatformBan correctly handles missing entries.Returning
false
when the masternode entry does not exist (line 194) is appropriate—you cannot reset a ban on a nonexistent entry.
199-210
: LGTM: SetPlatformBan creates entry if needed and caches ban message.The method correctly uses
GetMetaInfo
(which creates an entry if absent), attempts the ban update, and on success caches the ban message. The move semantics forban_msg
are efficient.src/masternode/meta.h (9)
29-46
: LGTM: Public member variables align with value-based refactor.Exposing
CMasternodeMetaInfo
fields publicly is appropriate given the refactor removes per-entry locking and centralizes synchronization at the manager level (CMasternodeMetaMan::cs
).
56-61
: LGTM: Serialization includes all necessary fields.All members—including
m_protx_hash
, DSQ/mixing state, governance votes, outbound tracking, and platform ban state—are serialized. This preserves state across restarts.
69-70
: LGTM: Outbound attempt/success setters correctly update counters.
SetLastOutboundAttempt
increments the attempt counter, whileSetLastOutboundSuccess
resets it. This logic supports the "too many failed attempts" check.
72-83
: Verify platform ban state transition semantics.The
SetPlatformBan
logic rejects older heights (line 74-76) and rejects unbanning at the same height as a ban (line 77-79). Confirm this aligns with the intended platform ban state machine—specifically, that unbanning at the same height as banning should indeed fail.If the semantics are correct, consider adding a brief comment explaining why same-height unbans are rejected, as this is subtle.
92-92
: LGTM: Value-based map storage aligns with refactor objectives.Switching from
std::map<uint256, std::shared_ptr<CMasternodeMetaInfo>>
tostd::map<uint256, CMasternodeMetaInfo>
removes pointer indirection and simplifies memory management, as intended by this refactor.
102-103
: LGTM: Serialization correctly copies value-based entries.Iterating
metaInfos
and emplacingp.second
into the temporary vector is correct for the value-based map.
122-122
: LGTM: Unserialization efficiently moves entries into map.Using
CMasternodeMetaInfo{std::move(mm)}
avoids copying the unserialized object. The keymm.m_protx_hash
is extracted before the move, which is correct.
188-189
: LGTM: Private accessor lock annotations match implementations.The
EXCLUSIVE_LOCKS_REQUIRED(cs)
annotations correctly document that callers must holdcs
when invoking these private helpers.
199-227
: LGTM: Public API lock annotations correctly specify external requirements.All public methods use
EXCLUSIVE_LOCKS_REQUIRED(!cs)
, documenting that callers must not hold the lock (the methods acquire it internally). This prevents deadlocks and centralizes locking at the manager level.
Issue being fixed or feature implemented
Current implementation:
CMasternodeMetaInfo
is done by accessingGetMetaInfo
under mutexCMasternodeMetaMan::cs
GetMetaInfo
returns shared_ptr to objectGetMetaInfo
is protected by atomic, second mutexCMasternodeMetaInfo::cs
; access to object is done indirectly by one more memory hop (because shared_ptr)It is not efficient, because
GetMetaInfo
is spread all over code base and not a single case need shared_ptr here; all accesses are brief and short (single read or single write; except RPC where all structure is serialized).What was done?
This PR is follow-up for PR #6868 with further improvements and simplification of code.
Instead returning shared_ptr with
CMasternodeMetaInfo
directly access to members ofCMasternodeMetaMan
under its mutex; all mutexes, atomic and shared_ptr itself are removed fromCMasternodeMetaInfo
accesses.It simplified implementation significantly, removed big amount of thread-safety annotations.
Performance is probably improved, but improvement is unmeasurable, because
CMasternodeMetaInfo
andCMasternodeMetaMan
has not been spotted as hot spot in profiler neither hot spot in lock contentions logs.Also this PR removes duplicated code between coinjoin/client and coinjoin/server by creating a helper in
CMasternodeMetaMan
How Has This Been Tested?
Run unit & functional tests
Breaking Changes
N/A
Checklist: