Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Nov 25, 2025

Issue being fixed or feature implemented

Separation of consensus & chain code and network & node in Dash Core is blocked by tight connection of network and consensus code; in components such as llmq::CSigningManager, llmq::CSigSharesManager, coinjoin::client, coinjoin::server, governance/, llmq::CInstantSendManager, etc.
It blocks backport's of bitcoin related to 'kernel' project.

What was done?

This PR addresses a dependency of CGovernanceManager on PeerManager

It is a split from proof-of-concept PR #6934

Prior work: #6959 #6992

How Has This Been Tested?

Run unit & functional tests.
Run a linter test/lint/lint-circular-dependencies.py

Overall conception is tested in #6934 as potentially successful to finish separation of network & consensus code.

Breaking Changes

N/A

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

@knst knst added this to the 23.1 milestone Nov 25, 2025
@knst knst changed the title refactor: separate network and consensus logic 3/N [governance] refactor: separate network and consensus logic 3/N [governance] Nov 25, 2025
@github-actions
Copy link

github-actions bot commented Nov 25, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Walkthrough

The PR refactors governance networking by introducing NetGovernance as a NetHandler and centralizing handler scheduling via PeerManager::ScheduleHandlers. CGovernanceManager's network-facing API is simplified (ProcessMessage → ProcessObject) and new accessors added (FetchRelayInventory, FetchGovernanceObjectVotes, GetMNManager, ProcessVote) with updated locking annotations. NetHandler gains Schedule and ProcessMessage now accepts a CConnman& parameter. CMasternodeSync's governance tick/maintenance code is removed. LoadChainstate signatures drop governance/txindex/network-id parameters. Tests and lint expectations adjusted for changed dependency graph.

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler
    participant PeerManager
    participant NetGovernance
    participant CGovernanceManager
    participant Connman
    participant Node

    Note over Scheduler,PeerManager: Handler registration
    Scheduler->>PeerManager: ScheduleHandlers()
    PeerManager->>NetGovernance: Schedule(scheduler, connman)

    Note over Scheduler,NetGovernance: Periodic processing
    Scheduler->>NetGovernance: ProcessTick(connman)
    alt Need votes/objects
        NetGovernance->>CGovernanceManager: FetchGovernanceObjectVotes(...)
        NetGovernance->>Connman: send object/vote requests
    else Relay/housekeeping
        NetGovernance->>CGovernanceManager: FetchRelayInventory()
    end

    Note over Node,NetGovernance: Incoming messages
    PeerManager->>NetGovernance: ProcessMessage(node, connman, msg_type, vRecv)
    alt MNGOVERNANCEOBJECT
        NetGovernance->>CGovernanceManager: ProcessObject(node, hash, govobj)
    else MNGOVERNANCEOBJECTVOTE
        NetGovernance->>CGovernanceManager: ProcessVote(node, vote, exception, connman)
    end
    NetGovernance->>PeerManager: PeerPostProcessMessage(result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas to focus on:
    • src/governance/net_governance.{cpp,h} — new scheduling, request throttling, message handling
    • src/governance/governance.{h,cpp} — API signature changes, locking annotations, ProcessObject semantics
    • src/net_processing.{h,cpp} — NetHandler/PeerManager interface changes and call-site updates
    • All NetHandler implementations (e.g., instantsend, llmq) for updated ProcessMessage signature
    • Init/chainstate changes: src/init.cpp, src/node/chainstate.{h,cpp}, and tests referencing LoadChainstate
    • Thread-safety annotations (EXCLUSIVE_LOCKS_REQUIRED) correctness

Possibly related PRs

Suggested reviewers

  • UdjinM6
  • PastaPastaPasta
  • kwvg

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: refactoring to separate network and consensus logic in the governance component, part 3 of a series.
Description check ✅ Passed The description clearly explains the issue being addressed, the approach taken, testing performed, and references prior related work.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (9)
src/net_processing.cpp (1)

662-662: Clarify PeerPostProcessMessage semantics for Node-scoped side effects

PeerPostProcessMessage always calls PostProcessMessage(std::move(ret), -1), so any m_error/m_to_erase in MessageProcessingResult will not be attributed to a real peer (both Misbehaving and EraseObjectRequest effectively no-op for node -1).

If NetHandlers are expected to:

  • Drive misbehavior scoring or
  • Erase per-peer object requests

via MessageProcessingResult, they will currently not get those behaviors when going through PeerPostProcessMessage. In that case, either:

  • Thread the actual NodeId through PeerPostProcessMessage, or
  • Document that handlers must call PeerMisbehaving / PeerEraseObjectRequest directly and only use PeerPostProcessMessage for node-agnostic actions (m_transactions, m_inventory, m_dsq).

Please confirm which contract you intend and adjust either the API or callers accordingly.

Also applies to: 3587-3604, 6579-6582

src/governance/net_governance.cpp (7)

20-20: Unnecessary forward declaration.

CConnman is already declared in net.h which is included on line 12. This forward declaration is redundant.

-class CConnman;
-
 void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman)

131-131: Minor typo: "to early" → "too early".

-            // to early to ask the same node
+            // too early to ask the same node

161-162: Minor style: prefer static const over const static.

Both are valid C++, but static const is the more conventional ordering.

-    const static int64_t nSyncStart = TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now());
-    const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart);
+    static const int64_t nSyncStart = TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now());
+    static const std::string strAllow = strprintf("allow-sync-%lld", nSyncStart);

310-310: Minor typo: "to early" → "too early".

-            continue; // to early for this node
+            continue; // too early for this node

379-382: Redundant IsBlockchainSynced() check.

This check is redundant because line 348 already returns early if !m_node_sync.IsBlockchainSynced(). Consider removing for clarity.

-        if (!m_node_sync.IsBlockchainSynced()) {
-            LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECT -- masternode list not synced\n");
-            return;
-        }

409-412: Redundant IsBlockchainSynced() check.

Same as above - this is already guarded by the check on line 348.

-        if (!m_node_sync.IsBlockchainSynced()) {
-            LogPrint(BCLog::GOBJECT, "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n");
-            return;
-        }

440-441: Existing TODO comment about relay issue.

This TODO indicates a known issue with immediate inventory relay. Consider tracking this in an issue if not already done.

Would you like me to help document this issue or investigate why immediate sending doesn't work here?

src/governance/governance.h (1)

389-389: Minor: extra blank line.

     bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);

-
 private:
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d69b5a and dc1d437.

📒 Files selected for processing (17)
  • src/Makefile.am (2 hunks)
  • src/governance/governance.cpp (9 hunks)
  • src/governance/governance.h (1 hunks)
  • src/governance/net_governance.cpp (1 hunks)
  • src/governance/net_governance.h (1 hunks)
  • src/init.cpp (3 hunks)
  • src/instantsend/net_instantsend.cpp (1 hunks)
  • src/instantsend/net_instantsend.h (1 hunks)
  • src/masternode/sync.cpp (1 hunks)
  • src/masternode/sync.h (2 hunks)
  • src/net_processing.cpp (5 hunks)
  • src/net_processing.h (3 hunks)
  • src/node/chainstate.cpp (0 hunks)
  • src/node/chainstate.h (0 hunks)
  • src/test/util/setup_common.cpp (0 hunks)
  • src/validation.h (0 hunks)
  • test/lint/lint-circular-dependencies.py (2 hunks)
💤 Files with no reviewable changes (4)
  • src/test/util/setup_common.cpp
  • src/validation.h
  • src/node/chainstate.h
  • src/node/chainstate.cpp
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/masternode/sync.cpp
  • src/governance/net_governance.h
  • src/instantsend/net_instantsend.cpp
  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/net_processing.h
  • src/instantsend/net_instantsend.h
  • src/init.cpp
  • src/net_processing.cpp
  • src/governance/net_governance.cpp
  • src/governance/governance.h
src/{masternode,evo}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Masternode lists must use immutable data structures (Immer library) for thread safety

Files:

  • src/masternode/sync.cpp
  • src/masternode/sync.h
src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Files:

  • src/masternode/sync.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/governance/net_governance.cpp
  • src/governance/governance.h
src/{masternode,llmq}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

BLS integration must be used for cryptographic foundation of advanced masternode features

Files:

  • src/masternode/sync.cpp
  • src/masternode/sync.h
src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Files:

  • src/masternode/sync.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/governance/net_governance.cpp
  • src/governance/governance.h
src/governance/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Files:

  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/net_governance.cpp
  • src/governance/governance.h
🧠 Learnings (21)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Applied to files:

  • src/Makefile.am
  • src/masternode/sync.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/init.cpp
  • src/governance/net_governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/Makefile.am
  • src/governance/governance.cpp
  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/Makefile.am
  • src/governance/governance.cpp
  • src/net_processing.h
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/masternode/sync.cpp
  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/init.cpp
  • src/governance/governance.h
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket operations are properly synchronized using the cs_net mutex. The m_sock and m_server members are GUARDED_BY(cs_net), and methods like Connect(), SendDirectly(), and ReconnectThread() use appropriate locking with cs_net to prevent race conditions on socket access.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access requires synchronization using the cs_net mutex. The race condition exists between SendDirectly() (callable from any thread) and Connect() (called by ReconnectThread), where both read and write m_sock concurrently. The cs_net mutex properly coordinates these network operations.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks. The implementation correctly uses cs_net with GUARDED_BY annotations and EXCLUSIVE_LOCKS_REQUIRED to synchronize socket access between SendDirectly() and ReconnectThread().

Applied to files:

  • src/instantsend/net_instantsend.cpp
  • src/governance/governance.h
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access is protected by the cs_net mutex. This mutex coordinates between the TCP send path in SendDirectly() and the reconnection operations in Connect()/Reconnect() methods, ensuring proper synchronization of socket state.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket access operations (m_sock and m_server) should be protected by the cs_net mutex, not avoiding synchronization entirely. While lock overhead concerns are valid in general, socket operations require proper synchronization via cs_net.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-10-21T11:09:34.688Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6849
File: src/governance/governance.cpp:1339-1343
Timestamp: 2025-10-21T11:09:34.688Z
Learning: In PR #6849, `cmapVoteToObject` was intentionally moved from `GovernanceStore` to `CGovernanceManager` as a memory-only variable and is NOT guarded by `cs_store`. It relies on `CacheMap`'s internal thread-safety instead.

Applied to files:

  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq}/**/*.{cpp,h} : BLS integration must be used for cryptographic foundation of advanced masternode features

Applied to files:

  • src/governance/governance.cpp
  • src/masternode/sync.h
  • src/init.cpp
📚 Learning: 2025-10-28T08:54:00.392Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6926
File: test/functional/feature_governance_cl.py:0-0
Timestamp: 2025-10-28T08:54:00.392Z
Learning: In Dash tests, the scheduler (mockscheduler) operates independently of network state. Isolated nodes should still run scheduler-based cleanup processes like governance cleanup, even if they have different state due to network isolation.

Applied to files:

  • src/governance/governance.cpp
  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-11-04T18:23:28.175Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/governance/classes.cpp:147-154
Timestamp: 2025-11-04T18:23:28.175Z
Learning: In src/governance/classes.cpp, CSuperblock::GetPaymentsLimit intentionally uses synthetic difficulty constants (nBits = 1 for mainnet, powLimit for networks with min difficulty) and simple height-based V20 activation checks instead of actual chain block data. This is by design because superblocks themselves are "synthetic" governance payment blocks, not regular mined blocks.

Applied to files:

  • src/masternode/sync.h
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/masternode/sync.h
  • src/init.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/**/*.{cpp,h} : Special transactions use payload serialization routines defined in src/evo/specialtx.h and must include appropriate special transaction types (ProRegTx, ProUpServTx, ProUpRegTx, ProUpRevTx)

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : InstantSend implementation must provide distributed key generation for secure transaction locking with quorum consensus

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-13T20:02:55.480Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6969
File: src/evo/deterministicmns.h:441-479
Timestamp: 2025-11-13T20:02:55.480Z
Learning: In `src/evo/deterministicmns.h`, the `internalId` field in `CDeterministicMN` and the `mnInternalIdMap` in `CDeterministicMNList` are non-deterministic and used only for internal bookkeeping and efficient lookups. Different nodes can assign different internalIds to the same masternode depending on their sync history. Methods like `IsEqual()` intentionally ignore internalId mappings and only compare consensus-critical deterministic fields (proTxHash, collateral, state, etc.).

Applied to files:

  • src/governance/governance.h
🧬 Code graph analysis (7)
src/governance/net_governance.h (3)
src/governance/governance.cpp (2)
  • CGovernanceManager (66-79)
  • CGovernanceManager (81-85)
src/net_processing.h (3)
  • NetHandler (71-87)
  • NetHandler (75-78)
  • PeerManagerInternal (58-69)
src/governance/net_governance.cpp (10)
  • Schedule (22-54)
  • Schedule (22-22)
  • ProcessMessage (345-449)
  • ProcessMessage (345-345)
  • SendGovernanceSyncRequest (56-61)
  • SendGovernanceSyncRequest (56-56)
  • RequestGovernanceObjectVotes (63-152)
  • RequestGovernanceObjectVotes (63-63)
  • ProcessTick (154-343)
  • ProcessTick (154-154)
src/instantsend/net_instantsend.cpp (1)
src/net_processing.cpp (12)
  • ProcessMessage (3681-5471)
  • ProcessMessage (3681-3686)
  • pfrom (618-618)
  • pfrom (639-640)
  • pfrom (753-755)
  • pfrom (764-764)
  • pfrom (772-772)
  • pfrom (775-775)
  • pfrom (777-777)
  • pfrom (779-779)
  • pfrom (875-875)
  • pfrom (1057-1057)
src/governance/governance.cpp (2)
src/governance/governance.h (1)
  • nCachedBlockHeight (261-261)
src/net.h (1)
  • FullyConnectedOnly (1296-1296)
src/net_processing.h (1)
src/net_processing.cpp (26)
  • PeerGetRequestedObjectCount (6574-6577)
  • PeerGetRequestedObjectCount (6574-6574)
  • nodeid (628-628)
  • nodeid (631-631)
  • nodeid (644-644)
  • nodeid (656-656)
  • nodeid (661-661)
  • nodeid (679-679)
  • nodeid (681-681)
  • nodeid (719-720)
  • nodeid (730-730)
  • nodeid (862-862)
  • nodeid (864-864)
  • nodeid (1040-1040)
  • nodeid (1075-1075)
  • PeerPostProcessMessage (6579-6582)
  • PeerPostProcessMessage (6579-6579)
  • ret (662-662)
  • ret (685-685)
  • scheduler (624-624)
  • scheduler (652-652)
  • scheduler (694-694)
  • ProcessMessage (3681-5471)
  • ProcessMessage (3681-3686)
  • ScheduleHandlers (1701-1706)
  • ScheduleHandlers (1701-1701)
src/instantsend/net_instantsend.h (2)
src/net_processing.cpp (12)
  • ProcessMessage (3681-5471)
  • ProcessMessage (3681-3686)
  • pfrom (618-618)
  • pfrom (639-640)
  • pfrom (753-755)
  • pfrom (764-764)
  • pfrom (772-772)
  • pfrom (775-775)
  • pfrom (777-777)
  • pfrom (779-779)
  • pfrom (875-875)
  • pfrom (1057-1057)
src/instantsend/net_instantsend.cpp (2)
  • ProcessMessage (17-65)
  • ProcessMessage (17-17)
src/init.cpp (3)
src/interfaces/node.h (2)
  • node (42-44)
  • node (404-404)
src/rpc/blockchain.h (1)
  • node (33-36)
src/init.h (1)
  • node (23-25)
src/governance/net_governance.cpp (3)
src/util/time.h (1)
  • TicksSinceEpoch (86-89)
src/net.h (1)
  • FullyConnectedOnly (1296-1296)
src/masternode/sync.cpp (2)
  • ProcessMessage (99-112)
  • ProcessMessage (99-99)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (29)
src/Makefile.am (1)

222-223: Net governance unit correctly wired into build system

Adding governance/net_governance.h to BITCOIN_CORE_H and governance/net_governance.cpp to libbitcoin_node_a_SOURCES is consistent with how other governance components are exposed and linked; no issues from a build/ordering perspective.

Also applies to: 511-511

src/instantsend/net_instantsend.h (1)

34-38: NetInstantSend interface aligned with NetHandler and Stop/Interrupt contract

Updating ProcessMessage to accept CConnman& and adding an Interrupt() override that forwards to workInterrupt() cleanly integrates NetInstantSend with the new NetHandler interface and the existing Stop() assertion that expects Interrupt() to be called first. No functional regressions apparent.

src/instantsend/net_instantsend.cpp (1)

17-17: ProcessMessage signature update is consistent and behavior‑preserving

The implementation now takes CConnman& (unnamed) to match the updated NetHandler interface and net_processing call sites, while leaving the message handling logic unchanged. This maintains existing InstantSend behavior.

src/net_processing.cpp (3)

1701-1706: Handler scheduling loop is straightforward and thread-safe

ScheduleHandlers just iterates m_handlers and forwards scheduler and m_connman; there are no new locking or lifecycle hazards here given handlers are already managed via Start/Stop/Interrupt.


5462-5463: Ensure NetHandler::ProcessMessage’s contract matches ignoring its return value

handler->ProcessMessage(pfrom, m_connman, msg_type, vRecv); is now called for each m_handlers entry, and its return value (if any) is ignored, unlike the built-in handlers above which still funnel their MessageProcessingResult through PostProcessMessage.

Please verify that:

  • NetHandler::ProcessMessage has been changed to void (handlers do their own post-processing via Peer* APIs), or
  • If it still returns a MessageProcessingResult, that all relevant logic was migrated into the handlers and no behavior previously driven by the result is now lost.

If the latter, consider restoring a PostProcessMessage call on the result to keep the behavior consistent.


652-652: Scheduling API surface is cohesive with existing handler lifecycle

Adding ScheduleHandlers(CScheduler&) alongside StartHandlers/StopHandlers/InterruptHandlers maintains a clear responsibility split: construction, lifecycle, and periodic scheduling are all delegated through PeerManagerImpl. The current design keeps handler ownership and scheduling concerns centralized.

Also applies to: 1670-1699

src/masternode/sync.cpp (1)

7-8: Includes updated to match used types

Adding <chain.h> and <net.h> is appropriate here given this implementation uses CBlockIndex, CConnman, and CNode. This tightens dependencies without changing behavior.

src/init.cpp (2)

2229-2229: LGTM! NetGovernance handler correctly wired.

The handler is properly constructed with required dependencies and added after loading the governance cache. This follows the same pattern as NetInstantSend on line 2198.

Note: The NetGovernance::ProcessMessage and NetGovernance::Schedule implementations already check m_gov_manager.IsValid() internally (per src/governance/net_governance.cpp lines 23 and 344), so the handler can be safely added regardless of is_governance_enabled.


2301-2301: Centralized scheduling via ScheduleHandlers is a clean refactor.

This replaces governance-specific scheduling with a generalized mechanism that iterates over all registered NetHandler instances and calls their Schedule method. This improves separation of concerns.

src/net_processing.h (3)

67-68: Appropriate extension of PeerManagerInternal interface.

These methods expose necessary peer-level operations to NetHandler implementations:

  • PeerGetRequestedObjectCount: Allows handlers to check request queue size to prevent setAskFor overflow (used in RequestGovernanceObjectVotes)
  • PeerPostProcessMessage: Enables handlers to trigger post-processing for misbehavior tracking

Both are properly implemented in PeerManagerImpl per the relevant snippets.


83-84: NetHandler interface extended with scheduling and message processing hooks.

Adding CConnman& parameter to both methods is necessary for handlers to send network messages. The default empty implementations allow handlers to opt-in to only the functionality they need.


174-174: ScheduleHandlers provides centralized scheduling orchestration.

This completes the PeerManager interface to support the new handler scheduling pattern used in init.cpp.

src/governance/net_governance.h (1)

1-39: Well-structured new NetGovernance handler class.

The design cleanly separates governance networking concerns:

  • Proper inheritance from NetHandler with final specifier
  • Constructor dependency injection pattern matches the codebase style
  • References (not pointers) for required dependencies ensure non-null invariants
  • Private helpers encapsulate implementation details

Minor observation: The include guard uses BITCOIN_GOVERNANCE_NET_GOVERNANCE_H which follows the existing convention in the codebase.

test/lint/lint-circular-dependencies.py (2)

42-43: Circular dependency expectations correctly updated.

The paths now route through governance/governance directly instead of via masternode/active/context, reflecting the architectural change where governance networking is decoupled from the active masternode context.


53-53: The new circular dependency is a necessary consequence of the design, not unintended.

The cycle exists because CMasternodeSync has a CConnman& member variable (sync.h line 52) and calls methods on it in the implementation, requiring sync.cpp to include the full net.h definition. Conversely, multiple functions in CConnman take CMasternodeSync& parameters and appear to require the full definition, necessitating the include in net.cpp. This tight coupling is the minimal expected dependency given the architectural choice to embed CConnman& in CMasternodeSync. No refactoring is needed unless the underlying architecture changes.

src/masternode/sync.h (2)

22-28: Sync state constants properly exposed.

Moving these constants to the header allows NetGovernance and other components to reference sync states and timing values without duplicating magic numbers. The values are well-documented with comments.


67-73: New accessors enable external governance sync management.

These additions expose the minimum necessary internal state:

  • BumpAttempt(): Allows NetGovernance to track peer attempts
  • GetLastBump(): Enables timeout checking in external scheduling
  • IsReachedBestHeader(): Required for sync state decisions

This is a good pattern - exposing controlled accessors rather than making members public or passing the entire object.

src/governance/net_governance.cpp (3)

22-54: LGTM!

The scheduling logic correctly gates governance-specific tasks behind IsValid() and uses appropriate timing for test vs. production environments.


94-96: Static local state requires careful consideration.

Using static locals for cs_recently and mapAskedRecently means this state persists across all NetGovernance instances and is shared globally. While this appears intentional (to track recently-asked peers across sync sessions), ensure this is the desired behavior, especially if multiple NetGovernance instances could exist.


140-147: Verify loop termination when no nodes can be asked.

The i-- on line 146 compensates when fAsked is false, but since lines 141-145 unconditionally pop from the vectors regardless of fAsked, the loop will eventually terminate when vectors empty (line 116 breaks). This logic appears correct but is subtle.

src/governance/governance.cpp (8)

27-27: LGTM!

Include change from validation.h to validationinterface.h aligns with the PR's goal of separating concerns. The file uses GetMainSignals() from validationinterface.h for notifications.


183-231: LGTM with optional efficiency note.

The new ProcessObject method correctly consolidates governance object processing. The lock annotations properly require cs_main to be held by the caller (as shown in net_governance.cpp line 393 with WITH_LOCK(::cs_main, ...)).

For a future optimization, consider taking CGovernanceObject by rvalue reference since it's ultimately moved into storage, but this is fine for a refactoring PR.


449-455: LGTM!

Clean swap-and-return pattern for fetching relay inventory with proper locking.


599-599: LGTM!

Adding explicit LOCK(cs_store) at function start correctly implements the lock requirement for accessing mapObjects.


652-652: LGTM!

Same as above - proper lock acquisition for SyncObjects.


960-960: LGTM!

GetMNManager() correctly uses Assert to ensure m_dmnman is valid before dereferencing.


962-991: LGTM!

The updated FetchGovernanceObjectVotes returns a structured pair of vectors, providing a cleaner API than modifying parameters in-place.


1174-1198: LGTM!

The orphan vote cleanup logic is correctly inlined, and the NodesSnapshot pattern with FullyConnectedOnly matches usage in ProcessTick() (see net_governance.cpp line 179).

src/governance/governance.h (1)

367-388: LGTM! Clear API section for NetGovernance integration.

The new public methods are well-organized under a clear comment and have proper lock annotations. The API surface exposed to NetGovernance is appropriately scoped:

  • FetchRelayInventory() - relay coordination
  • CheckAndRemove() - cleanup operations
  • RequestOrphanObjects() - orphan management
  • FetchGovernanceObjectVotes() - vote request preparation
  • ProcessObject() / ProcessVote() - object/vote processing
  • SyncObjects() / SyncSingleObjVotes() - sync responses
  • AcceptMessage() - request tracking
  • GetMNManager() - MN list access

Comment on lines +17 to +18
/** Default for -syncmempool */
static const bool DEFAULT_SYNC_MEMPOOL = true;
Copy link

@coderabbitai coderabbitai bot Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

DEFAULT_SYNC_MEMPOOL constant placement.

This constant is now defined in the masternode/sync header. Verify this is the intended location, as it appears to be a general node configuration option rather than masternode-specific.


🏁 Script executed:

#!/bin/bash
# Check where DEFAULT_SYNC_MEMPOOL was previously defined and where it's used
rg -n "DEFAULT_SYNC_MEMPOOL" --type cpp --type h

Length of output: 383


I'll verify whether this location is appropriate by checking the pattern for similar configuration constants.

Actually, let me correct that - I need to search the actual codebase:


🏁 Script executed:

#!/bin/bash
# Check pattern of DEFAULT_* constants and their locations
echo "=== Finding DEFAULT_* constants ==="
rg -n "static const.*DEFAULT_" --type cpp --type h | head -20

echo ""
echo "=== Checking what init.cpp includes for constants ==="
rg -n "^#include" src/init.cpp | head -20

echo ""
echo "=== Checking where other mempool constants are defined ==="
rg -n "mempool" --type h src/ | grep -i "const\|define" | head -15

Length of output: 4544


🏁 Script executed:

This will be run on the codebase to check patterns.

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check pattern of DEFAULT_* constants and their locations
echo "=== Finding DEFAULT_* constants ==="
rg -n "static const.*DEFAULT_" --type cpp --type h | head -20

echo ""
echo "=== Checking what init.cpp includes ==="
head -50 src/init.cpp | rg "^#include"

echo ""
echo "=== Check other DEFAULT_ constants in masternode/sync.h and nearby files ==="
rg -n "DEFAULT_" src/masternode/ --type cpp --type h | head -15

Length of output: 2768


Let me check if this constant was recently added or moved to this location.


🏁 Script executed:

#!/bin/bash
# Check if validation.h is included by masternode/sync.h or other related files
echo "=== Checking masternode/sync.h includes ==="
head -30 src/masternode/sync.h

echo ""
echo "=== Check validation.h for mempool-related constants ==="
rg -n "DEFAULT_.*MEMPOOL|DEFAULT_.*SYNC" src/validation.h

echo ""
echo "=== Look for any other instances of DEFAULT_SYNC in codebase ==="
rg -n "DEFAULT_SYNC" --type cpp --type h

Length of output: 1813


Move DEFAULT_SYNC_MEMPOOL to src/validation.h with other mempool-related defaults.

The constant is placed in src/masternode/sync.h but should follow the established pattern: similar mempool constants like DEFAULT_MEMPOOL_EXPIRY and DEFAULT_PERSIST_MEMPOOL are defined in src/validation.h. The -syncmempool option is a general node configuration (used in src/init.cpp) and governance (src/governance/net_governance.cpp), not masternode-specific functionality. This placement should be moved to maintain proper separation of concerns and code organization.

🤖 Prompt for AI Agents
In src/masternode/sync.h around lines 17-18, the DEFAULT_SYNC_MEMPOOL constant
belongs in src/validation.h with the other mempool defaults; remove the static
const bool DEFAULT_SYNC_MEMPOOL = true; from sync.h, add the same definition
into src/validation.h next to DEFAULT_MEMPOOL_EXPIRY/DEFAULT_PERSIST_MEMPOOL,
and ensure any files that previously relied on including sync.h for this symbol
(e.g., src/init.cpp, src/governance/net_governance.cpp) include src/validation.h
instead; rebuild to verify no missing symbol or include errors.

Copy link
Collaborator Author

@knst knst Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct; the file masternode/sync.h which have CMasternodeSync is incorrectly named and incorrectly placed. It has nothing to do with "masternode".

It should be called NodeSyncing or NodeSyncStatus or something similar and be located in node/sync.h.

DEFAULT_SYNC_MEMPOOL is correctly located now. it has nothing to do with "validation" in validation.h

Renaming it is out-of-scope of this PR; could be done someday later

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@knst, thank you for the clarification! You're absolutely right that DEFAULT_SYNC_MEMPOOL belongs with the synchronization logic conceptually, not with validation. I understand now that while the file naming and location could be improved (as a future refactoring to node/sync.h with a class rename), the constant is correctly co-located with the sync functionality it relates to.

I appreciate you explaining the architectural reasoning—this helps me better understand the separation between sync-related configuration and validation-related configuration.

 (\__/)
 (='.'=)  *noted for future reviews*
 (")_(")

✏️ Learnings added
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.501Z
Learning: In Dash Core, `DEFAULT_SYNC_MEMPOOL` belongs with node synchronization logic (currently in src/masternode/sync.h) rather than with validation logic in src/validation.h, even though other mempool-related constants are in validation.h. The constant is conceptually tied to sync functionality, not validation.

Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.501Z
Learning: The file src/masternode/sync.h containing `CMasternodeSync` is misnamed and misplaced—it has nothing to do with "masternode" functionality. It should eventually be renamed to `NodeSyncing` or `NodeSyncStatus` and moved to src/node/sync.h as a future refactoring.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMasternodeSync is incorrectly named and incorrectly placed. It has nothing to do with "masternode".

To provide some historical context: it was originally used to sync masternode list before DIP0003 and before any governance code existed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

void PeerRelayInvFiltered(const CInv& inv, const CTransaction& relatedTx) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void PeerAskPeersForTransaction(const uint256& txid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
size_t PeerGetRequestedObjectCount(NodeId nodeid) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
Copy link

@coderabbitai coderabbitai bot Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix missing cs_main lock in PeerGetRequestedObjectCount wrapper

GetRequestedObjectCount asserts cs_main and reads m_node_states (GUARDED_BY(cs_main)), but PeerGetRequestedObjectCount calls it without taking cs_main, which will:

  • Trip AssertLockHeld(cs_main) in debug builds.
  • Cause a data race on m_node_states in release builds.

Wrap the call in a cs_main lock (and keep the !m_peer_mutex precondition to preserve lock order), e.g.:

 size_t PeerManagerImpl::PeerGetRequestedObjectCount(NodeId nodeid) const
 {
-    return GetRequestedObjectCount(nodeid);
+    WITH_LOCK(cs_main, return GetRequestedObjectCount(nodeid));
 }

This aligns the wrapper with the callee’s locking contract.

Also applies to: 1650-1658, 6574-6577

🤖 Prompt for AI Agents
In src/net_processing.cpp around line 661 (and similarly at 1650-1658 and
6574-6577), the PeerGetRequestedObjectCount wrapper calls
GetRequestedObjectCount without holding cs_main even though
GetRequestedObjectCount asserts cs_main and reads m_node_states
(GUARDED_BY(cs_main)); update each wrapper to acquire cs_main while preserving
the existing !m_peer_mutex precondition/lock ordering, call
GetRequestedObjectCount while cs_main is held, then release cs_main before
returning so the wrapper satisfies the callee’s locking contract and avoids
assert/data-race.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cs_main is hold correctly, see:

        WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(peer.GetId(), CInv{MSG_GOVERNANCE_OBJECT, nHash}));

Annotation exists also:

    void PeerEraseObjectRequest(const NodeId nodeid, const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@knst Thanks for the clarification — agreed on PeerEraseObjectRequest: it’s annotated EXCLUSIVE_LOCKS_REQUIRED(::cs_main) and the call site holds cs_main.

The concern I raised is specifically about PeerGetRequestedObjectCount. Its interface is only EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex), but it calls GetRequestedObjectCount(), which asserts cs_main and reads m_node_states (GUARDED_BY(cs_main)). Since callers of PeerGetRequestedObjectCount are not required to hold cs_main, this can trip the AssertLockHeld(cs_main) in debug or race in release.

Minimal, safe fix (preserves the existing precondition and lock ordering):

diff --git a/src/net_processing.cpp b/src/net_processing.cpp
@@
 size_t PeerManagerImpl::PeerGetRequestedObjectCount(NodeId nodeid) const
 {
-    return GetRequestedObjectCount(nodeid);
+    // Wrapper does not require cs_main; the callee asserts cs_main.
+    // Take cs_main here to satisfy the callee’s locking contract while keeping
+    // the !m_peer_mutex precondition and lock order intact.
+    WITH_LOCK(::cs_main, return GetRequestedObjectCount(nodeid));
 }

If you prefer the alternative of making cs_main an explicit requirement for the wrapper, we can also tighten the annotation in the interface to EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !m_peer_mutex) and adjust call sites accordingly. Let me know which direction you’d like; I can update the patch.


🧠 Learnings used
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket access operations (m_sock and m_server) should be protected by the cs_net mutex, not avoiding synchronization entirely. While lock overhead concerns are valid in general, socket operations require proper synchronization via cs_net.

Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks. The implementation correctly uses cs_net with GUARDED_BY annotations and EXCLUSIVE_LOCKS_REQUIRED to synchronize socket access between SendDirectly() and ReconnectThread().

Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.

Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Copy link

@coderabbitai coderabbitai bot left a 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 (4)
src/governance/net_governance.cpp (3)

20-20: Redundant forward declaration.

CConnman is already included transitively through the headers. This forward declaration is unnecessary.

-class CConnman;
-
 void NetGovernance::Schedule(CScheduler& scheduler, CConnman& connman)

131-132: Typo: "to early" should be "too early".

-            // to early to ask the same node
+            // too early to ask the same node
             if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue;

154-171: Static local variables limit testability.

The static variables (nTick, nSyncStart, strAllow, nTimeLastProcess) persist across calls and would be shared if multiple NetGovernance instances existed. This limits unit test isolation.

Consider promoting these to member variables in a follow-up PR to improve testability, though this is acceptable for a refactoring PR focused on code movement.

src/governance/governance.cpp (1)

183-231: Consider adding lock assertion for clarity.

The method is declared with EXCLUSIVE_LOCKS_REQUIRED(::cs_main, ...) and is correctly called with cs_main held in NetGovernance::ProcessMessage. However, adding an explicit assertion at the method entry would make the requirement self-documenting and catch misuse earlier (before the assertion in AddGovernanceObjectInternal).

 bool CGovernanceManager::ProcessObject(const CNode& peer, const uint256& nHash, CGovernanceObject govobj)
 {
+    AssertLockHeld(::cs_main);
     std::string strHash = nHash.ToString();

     LOCK(cs_store);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc1d437 and 094a3f6.

📒 Files selected for processing (4)
  • src/governance/governance.cpp (9 hunks)
  • src/governance/governance.h (1 hunks)
  • src/governance/net_governance.cpp (1 hunks)
  • src/governance/net_governance.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/governance/net_governance.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/governance.h
src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Files:

  • src/governance/net_governance.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/governance.h
src/governance/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Files:

  • src/governance/net_governance.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/governance.h
src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Files:

  • src/governance/net_governance.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/governance.h
🧠 Learnings (12)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Applied to files:

  • src/governance/net_governance.cpp
  • src/governance/net_governance.h
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq}/**/*.{cpp,h} : BLS integration must be used for cryptographic foundation of advanced masternode features

Applied to files:

  • src/governance/governance.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/governance/governance.cpp
📚 Learning: 2025-10-28T08:54:00.392Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6926
File: test/functional/feature_governance_cl.py:0-0
Timestamp: 2025-10-28T08:54:00.392Z
Learning: In Dash tests, the scheduler (mockscheduler) operates independently of network state. Isolated nodes should still run scheduler-based cleanup processes like governance cleanup, even if they have different state due to network isolation.

Applied to files:

  • src/governance/governance.cpp
📚 Learning: 2025-10-21T11:09:34.688Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6849
File: src/governance/governance.cpp:1339-1343
Timestamp: 2025-10-21T11:09:34.688Z
Learning: In PR #6849, `cmapVoteToObject` was intentionally moved from `GovernanceStore` to `CGovernanceManager` as a memory-only variable and is NOT guarded by `cs_store`. It relies on `CacheMap`'s internal thread-safety instead.

Applied to files:

  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-13T20:02:55.480Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6969
File: src/evo/deterministicmns.h:441-479
Timestamp: 2025-11-13T20:02:55.480Z
Learning: In `src/evo/deterministicmns.h`, the `internalId` field in `CDeterministicMN` and the `mnInternalIdMap` in `CDeterministicMNList` are non-deterministic and used only for internal bookkeeping and efficient lookups. Different nodes can assign different internalIds to the same masternode depending on their sync history. Methods like `IsEqual()` intentionally ignore internalId mappings and only compare consensus-critical deterministic fields (proTxHash, collateral, state, etc.).

Applied to files:

  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/governance/governance.h
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks. The implementation correctly uses cs_net with GUARDED_BY annotations and EXCLUSIVE_LOCKS_REQUIRED to synchronize socket access between SendDirectly() and ReconnectThread().

Applied to files:

  • src/governance/governance.h
📚 Learning: 2025-11-04T18:23:28.175Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/governance/classes.cpp:147-154
Timestamp: 2025-11-04T18:23:28.175Z
Learning: In src/governance/classes.cpp, CSuperblock::GetPaymentsLimit intentionally uses synthetic difficulty constants (nBits = 1 for mainnet, powLimit for networks with min difficulty) and simple height-based V20 activation checks instead of actual chain block data. This is by design because superblocks themselves are "synthetic" governance payment blocks, not regular mined blocks.

Applied to files:

  • src/governance/governance.h
🧬 Code graph analysis (3)
src/governance/net_governance.h (4)
src/governance/governance.cpp (2)
  • CGovernanceManager (66-79)
  • CGovernanceManager (81-85)
src/masternode/sync.cpp (4)
  • CMasternodeSync (14-20)
  • CMasternodeSync (22-22)
  • ProcessMessage (99-112)
  • ProcessMessage (99-99)
src/net_processing.h (3)
  • NetHandler (71-87)
  • NetHandler (75-78)
  • PeerManagerInternal (58-69)
src/governance/net_governance.cpp (10)
  • Schedule (22-54)
  • Schedule (22-22)
  • ProcessMessage (345-449)
  • ProcessMessage (345-345)
  • SendGovernanceSyncRequest (56-61)
  • SendGovernanceSyncRequest (56-56)
  • RequestGovernanceObjectVotes (63-152)
  • RequestGovernanceObjectVotes (63-63)
  • ProcessTick (154-343)
  • ProcessTick (154-154)
src/governance/governance.cpp (2)
src/governance/governance.h (1)
  • nCachedBlockHeight (261-261)
src/coinjoin/client.h (1)
  • nCachedBlockHeight (264-264)
src/governance/governance.h (2)
src/governance/governance.cpp (10)
  • FetchRelayInventory (449-455)
  • FetchRelayInventory (449-449)
  • AcceptMessage (993-1005)
  • AcceptMessage (993-993)
  • ProcessObject (183-231)
  • ProcessObject (183-183)
  • GetMNManager (960-960)
  • GetMNManager (960-960)
  • ProcessVote (800-858)
  • ProcessVote (800-800)
src/governance/object.cpp (2)
  • ProcessVote (58-159)
  • ProcessVote (58-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
🔇 Additional comments (6)
src/governance/net_governance.h (1)

14-37: Clean NetHandler implementation for governance networking.

The NetGovernance class properly derives from NetHandler and follows the established pattern for network handlers. The dependency injection via constructor references is appropriate, and the interface is well-defined.

src/governance/net_governance.cpp (2)

22-54: Well-structured scheduling with appropriate guards.

The scheduling logic properly:

  • Checks ShutdownRequested() before processing ticks
  • Conditionally schedules housekeeping only when governance is valid
  • Uses different timing for test vs. production networks

345-448: Governance message handling properly migrated.

The ProcessMessage method correctly handles all three governance message types with appropriate validation and peer misbehavior scoring. The ProcessObject call at line 393 properly acquires cs_main via WITH_LOCK as required by the method's lock annotations.

src/governance/governance.cpp (2)

449-455: Efficient swap-based inventory transfer.

Using swap to transfer the relay inventory avoids unnecessary copying while atomically clearing the source vector under the lock.


1174-1188: Good addition of orphan vote cleanup.

The orphan vote cleanup properly removes expired entries before requesting objects, preventing accumulation of stale data. The iterator handling correctly saves the current position before advancing.

src/governance/governance.h (1)

367-388: Well-organized public API expansion for NetGovernance.

The new public methods are properly grouped with a clarifying comment and have consistent lock annotations that match their implementations. The separation of governance networking concerns into NetGovernance while exposing necessary accessors maintains good encapsulation.

@knst knst force-pushed the refactor-peermanager-handlers-gov branch from 094a3f6 to a5ef0da Compare November 25, 2025 14:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/governance/governance.cpp (1)

183-231: Clarify locking expectations for ProcessObject (cs_main precondition).

ProcessObject assumes ::cs_main is held by the caller (as done in NetGovernance::ProcessMessage via WITH_LOCK(::cs_main, ...)) but only locks cs_store locally. This works with current call sites and keeps AddGovernanceObjectInternal’s lock assertions satisfied, but nothing in this method enforces the cs_main precondition.

To future‑proof against accidental misuse, consider adding an AssertLockHeld(::cs_main); at the top of ProcessObject and documenting that it must be called with cs_main held. Behavior stays identical, but incorrect future call sites will fail fast in debug builds.

src/instantsend/net_instantsend.cpp (1)

17-65: ProcessMessage signature alignment looks good.

Updating NetInstantSend::ProcessMessage to take CConnman& matches the new NetHandler interface and PeerManager dispatch. The unused parameter is fine; if compiler warnings become noisy, you can mark it [[maybe_unused]] or name it and (void)connman;, but that’s optional.

src/governance/governance.h (1)

389-390: Minor: Extra blank line.

There's an extra blank line before the private: section that could be removed for consistency.

     bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_store);

-
 private:
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 094a3f6 and a5ef0da.

📒 Files selected for processing (17)
  • src/Makefile.am (2 hunks)
  • src/governance/governance.cpp (9 hunks)
  • src/governance/governance.h (1 hunks)
  • src/governance/net_governance.cpp (1 hunks)
  • src/governance/net_governance.h (1 hunks)
  • src/init.cpp (3 hunks)
  • src/instantsend/net_instantsend.cpp (1 hunks)
  • src/instantsend/net_instantsend.h (1 hunks)
  • src/masternode/sync.cpp (1 hunks)
  • src/masternode/sync.h (2 hunks)
  • src/net_processing.cpp (5 hunks)
  • src/net_processing.h (3 hunks)
  • src/node/chainstate.cpp (0 hunks)
  • src/node/chainstate.h (0 hunks)
  • src/test/util/setup_common.cpp (0 hunks)
  • src/validation.h (0 hunks)
  • test/lint/lint-circular-dependencies.py (2 hunks)
💤 Files with no reviewable changes (4)
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/test/util/setup_common.cpp
  • src/validation.h
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/instantsend/net_instantsend.h
  • src/Makefile.am
  • src/masternode/sync.cpp
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/net_processing.h
  • src/instantsend/net_instantsend.cpp
  • src/governance/net_governance.h
  • src/governance/net_governance.cpp
  • src/init.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
  • src/masternode/sync.h
  • src/net_processing.cpp
src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Files:

  • src/governance/net_governance.h
  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
  • src/masternode/sync.h
src/governance/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Files:

  • src/governance/net_governance.h
  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Files:

  • src/governance/net_governance.h
  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
  • src/masternode/sync.h
src/{masternode,evo}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Masternode lists must use immutable data structures (Immer library) for thread safety

Files:

  • src/masternode/sync.h
src/{masternode,llmq}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

BLS integration must be used for cryptographic foundation of advanced masternode features

Files:

  • src/masternode/sync.h
🧠 Learnings (30)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/net_processing.h
  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket operations are properly synchronized using the cs_net mutex. The m_sock and m_server members are GUARDED_BY(cs_net), and methods like Connect(), SendDirectly(), and ReconnectThread() use appropriate locking with cs_net to prevent race conditions on socket access.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access requires synchronization using the cs_net mutex. The race condition exists between SendDirectly() (callable from any thread) and Connect() (called by ReconnectThread), where both read and write m_sock concurrently. The cs_net mutex properly coordinates these network operations.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks. The implementation correctly uses cs_net with GUARDED_BY annotations and EXCLUSIVE_LOCKS_REQUIRED to synchronize socket access between SendDirectly() and ReconnectThread().

Applied to files:

  • src/instantsend/net_instantsend.cpp
  • src/governance/governance.h
  • src/net_processing.cpp
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access is protected by the cs_net mutex. This mutex coordinates between the TCP send path in SendDirectly() and the reconnection operations in Connect()/Reconnect() methods, ensuring proper synchronization of socket state.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket access operations (m_sock and m_server) should be protected by the cs_net mutex, not avoiding synchronization entirely. While lock overhead concerns are valid in general, socket operations require proper synchronization via cs_net.

Applied to files:

  • src/instantsend/net_instantsend.cpp
  • src/net_processing.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Applied to files:

  • src/governance/net_governance.h
  • src/governance/net_governance.cpp
  • src/init.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/init.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/init.cpp
  • src/governance/governance.h
  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/**/*.{cpp,h} : Special transactions use payload serialization routines defined in src/evo/specialtx.h and must include appropriate special transaction types (ProRegTx, ProUpServTx, ProUpRegTx, ProUpRevTx)

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq}/**/*.{cpp,h} : BLS integration must be used for cryptographic foundation of advanced masternode features

Applied to files:

  • src/init.cpp
  • src/governance/governance.cpp
  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : InstantSend implementation must provide distributed key generation for secure transaction locking with quorum consensus

Applied to files:

  • src/init.cpp
📚 Learning: 2025-10-28T08:54:00.392Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6926
File: test/functional/feature_governance_cl.py:0-0
Timestamp: 2025-10-28T08:54:00.392Z
Learning: In Dash tests, the scheduler (mockscheduler) operates independently of network state. Isolated nodes should still run scheduler-based cleanup processes like governance cleanup, even if they have different state due to network isolation.

Applied to files:

  • src/init.cpp
  • src/governance/governance.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/governance/governance.cpp
  • src/masternode/sync.h
📚 Learning: 2025-11-25T10:53:37.523Z
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: The file src/masternode/sync.h containing `CMasternodeSync` is misnamed and misplaced—it has nothing to do with "masternode" functionality. It should eventually be renamed to `NodeSyncing` or `NodeSyncStatus` and moved to src/node/sync.h as a future refactoring.

Applied to files:

  • src/governance/governance.cpp
  • src/masternode/sync.h
📚 Learning: 2025-11-25T10:53:37.523Z
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: In Dash Core, `DEFAULT_SYNC_MEMPOOL` belongs with node synchronization logic (currently in src/masternode/sync.h) rather than with validation logic in src/validation.h, even though other mempool-related constants are in validation.h. The constant is conceptually tied to sync functionality, not validation.

Applied to files:

  • src/governance/governance.cpp
  • src/masternode/sync.h
📚 Learning: 2025-10-21T11:09:34.688Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6849
File: src/governance/governance.cpp:1339-1343
Timestamp: 2025-10-21T11:09:34.688Z
Learning: In PR #6849, `cmapVoteToObject` was intentionally moved from `GovernanceStore` to `CGovernanceManager` as a memory-only variable and is NOT guarded by `cs_store`. It relies on `CacheMap`'s internal thread-safety instead.

Applied to files:

  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-13T20:02:55.480Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6969
File: src/evo/deterministicmns.h:441-479
Timestamp: 2025-11-13T20:02:55.480Z
Learning: In `src/evo/deterministicmns.h`, the `internalId` field in `CDeterministicMN` and the `mnInternalIdMap` in `CDeterministicMNList` are non-deterministic and used only for internal bookkeeping and efficient lookups. Different nodes can assign different internalIds to the same masternode depending on their sync history. Methods like `IsEqual()` intentionally ignore internalId mappings and only compare consensus-critical deterministic fields (proTxHash, collateral, state, etc.).

Applied to files:

  • src/governance/governance.h
📚 Learning: 2025-11-04T18:23:28.175Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/governance/classes.cpp:147-154
Timestamp: 2025-11-04T18:23:28.175Z
Learning: In src/governance/classes.cpp, CSuperblock::GetPaymentsLimit intentionally uses synthetic difficulty constants (nBits = 1 for mainnet, powLimit for networks with min difficulty) and simple height-based V20 activation checks instead of actual chain block data. This is by design because superblocks themselves are "synthetic" governance payment blocks, not regular mined blocks.

Applied to files:

  • src/governance/governance.h
  • src/masternode/sync.h
📚 Learning: 2025-11-04T18:24:27.241Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-10-05T20:38:28.457Z
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-07-29T14:32:48.369Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h} : Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-08-19T14:57:31.801Z
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR #6692, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR #6789 to address it, consistent with avoiding scope creep in performance-focused PRs.

Applied to files:

  • src/net_processing.cpp
📚 Learning: 2025-07-29T14:33:01.040Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/wallet/wallet.cpp:0-0
Timestamp: 2025-07-29T14:33:01.040Z
Learning: In refactoring PRs like #6761, kwvg acknowledges code safety improvements (like null pointer checks and unused parameter warnings) but prefers to defer them to follow-up PRs to maintain focus on the primary refactoring objectives, avoiding scope creep.

Applied to files:

  • src/net_processing.cpp
🧬 Code graph analysis (7)
src/net_processing.h (1)
src/net_processing.cpp (36)
  • PeerGetRequestedObjectCount (6574-6577)
  • PeerGetRequestedObjectCount (6574-6574)
  • nodeid (628-628)
  • nodeid (631-631)
  • nodeid (644-644)
  • nodeid (656-656)
  • nodeid (661-661)
  • nodeid (679-679)
  • nodeid (681-681)
  • nodeid (719-720)
  • nodeid (730-730)
  • nodeid (862-862)
  • nodeid (864-864)
  • nodeid (1040-1040)
  • nodeid (1075-1075)
  • PeerPostProcessMessage (6579-6582)
  • PeerPostProcessMessage (6579-6579)
  • ret (662-662)
  • ret (685-685)
  • scheduler (624-624)
  • scheduler (652-652)
  • scheduler (694-694)
  • ProcessMessage (3681-5471)
  • ProcessMessage (3681-3686)
  • pfrom (618-618)
  • pfrom (639-640)
  • pfrom (753-755)
  • pfrom (764-764)
  • pfrom (772-772)
  • pfrom (775-775)
  • pfrom (777-777)
  • pfrom (779-779)
  • pfrom (875-875)
  • pfrom (1057-1057)
  • ScheduleHandlers (1701-1706)
  • ScheduleHandlers (1701-1701)
src/instantsend/net_instantsend.cpp (1)
src/net_processing.cpp (12)
  • ProcessMessage (3681-5471)
  • ProcessMessage (3681-3686)
  • pfrom (618-618)
  • pfrom (639-640)
  • pfrom (753-755)
  • pfrom (764-764)
  • pfrom (772-772)
  • pfrom (775-775)
  • pfrom (777-777)
  • pfrom (779-779)
  • pfrom (875-875)
  • pfrom (1057-1057)
src/governance/net_governance.h (2)
src/net_processing.h (3)
  • NetHandler (71-87)
  • NetHandler (75-78)
  • PeerManagerInternal (58-69)
src/governance/net_governance.cpp (10)
  • Schedule (22-54)
  • Schedule (22-22)
  • ProcessMessage (345-449)
  • ProcessMessage (345-345)
  • SendGovernanceSyncRequest (56-61)
  • SendGovernanceSyncRequest (56-56)
  • RequestGovernanceObjectVotes (63-152)
  • RequestGovernanceObjectVotes (63-63)
  • ProcessTick (154-343)
  • ProcessTick (154-154)
src/governance/net_governance.cpp (1)
src/net.h (1)
  • FullyConnectedOnly (1296-1296)
src/init.cpp (5)
src/net_processing.cpp (10)
  • node (616-616)
  • node (617-617)
  • node (642-642)
  • node (757-757)
  • node (792-792)
  • node (892-896)
  • node (907-907)
  • node (918-918)
  • node (929-929)
  • node (937-937)
src/interfaces/node.h (2)
  • node (42-44)
  • node (404-404)
src/kernel/coinstats.h (1)
  • node (19-21)
src/rpc/blockchain.h (1)
  • node (33-36)
src/init.h (1)
  • node (23-25)
src/governance/governance.cpp (1)
src/governance/governance.h (1)
  • nCachedBlockHeight (261-261)
src/masternode/sync.h (1)
src/masternode/sync.cpp (6)
  • BumpAssetLastTime (43-48)
  • BumpAssetLastTime (43-43)
  • GetAssetName (50-59)
  • GetAssetName (50-50)
  • GetSyncStatus (89-97)
  • GetSyncStatus (89-89)
🔇 Additional comments (15)
src/net_processing.h (1)

58-69: Handler and peer APIs are consistent with the new scheduling/message flow.

The additions to PeerManagerInternal (PeerGetRequestedObjectCount, PeerPostProcessMessage), NetHandler (Schedule, ProcessMessage with CConnman&), and PeerManager::ScheduleHandlers match the pattern implemented in PeerManagerImpl and used by NetGovernance/NetInstantSend. The abstractions keep network plumbing inside PeerManager while letting individual handlers own their scheduling and message handling.

Also applies to: 71-87, 155-175

src/governance/net_governance.h (1)

14-37: NetGovernance handler design matches the refactoring goal.

NetGovernance encapsulates governance P2P logic behind the generic NetHandler interface and is wired via constructor injection into the existing managers, which is exactly what’s needed to decouple governance consensus state from PeerManager. The Schedule / ProcessMessage overrides match the base class and the implementation in net_governance.cpp.

src/init.cpp (2)

88-92: NetGovernance attachment and gating look correct.

NetGovernance is always added as an extra handler, but is_governance_enabled is still respected by only loading the governance cache when enabled, and the handler’s implementation guards work on m_gov_manager.IsValid() / sync state. That keeps the wiring simple in init.cpp while avoiding governance network activity (and DB usage) when validation is disabled.

Also applies to: 1690-1691, 2212-2229


2292-2302: Scheduler integration via ScheduleHandlers is a clean generalization.

Calling node.peerman->ScheduleHandlers(*node.scheduler); after registering all handlers (InstantSend, NetGovernance, etc.) centralizes their periodic work (governance cleanup, orphan requests, relay batching, etc.) behind the NetHandler abstraction instead of hard-coding per-subsystem schedule blocks in init.cpp. The order relative to LLQM/masternode scheduling and the shutdown sequence is consistent with existing patterns.

test/lint/lint-circular-dependencies.py (1)

41-53: Updated circular-dependency expectations are consistent with the new handler wiring.

The added cycles capturing core_io → evo/mnhftx → llmq/signing → net_processing → governance/* → core_io and masternode/sync → net → masternode/sync reflect the new NetGovernance/handler paths and sync wiring without changing the linter logic. The whitelist stays aligned with the current architecture.

src/masternode/sync.h (1)

17-18: Sync constants and CMasternodeSync accessors are consistent with current design.

Defining DEFAULT_SYNC_MEMPOOL alongside other sync-related constants and exposing BumpAttempt(), GetLastBump(), and IsReachedBestHeader() mirrors the existing implementation in sync.cpp and keeps node-sync status queryable without changing behavior. Placement of the constant here matches the intended association with node synchronization rather than validation.

Also applies to: 65-74

src/governance/governance.cpp (1)

449-455: LGTM on new relay/vote fetch helpers and orphan‑vote cleanup.

  • FetchRelayInventory() correctly batches and drains m_relay_invs under cs_relay, called within scheduler-driven relay path in NetGovernance.
  • FetchGovernanceObjectVotes() safely co-operates with external NetGovernance code by cleaning expired mapAskedRecently entries using the advance-then-erase pattern while holding cs_store, and honoring nPeersPerHashMax before processing objects.
  • RequestOrphanObjects()'s pruning loop over cmmapOrphanVotes.GetItemList() correctly removes expired orphan votes using the advance-then-erase pattern to avoid iterator invalidation before requesting.

These changes improve separation between network handler code and governance state while maintaining scheduler-driven, network-decoupled voting architecture, without altering consensus behavior.

src/governance/net_governance.cpp (4)

22-54: Scheduler setup looks good; consider lifetime assumptions of captured references

The three scheduled tasks mirror the expected 1s tick plus slower housekeeping and relay pacing, and the IsValid() gate on the heavier governance maintenance/relay work makes sense.

One thing to double‑check: this and &connman are captured by reference in lambdas that may outlive NetGovernance if shutdown/teardown ordering ever changes. If Schedule is called once at init and NetGovernance/CConnman both live until scheduler shutdown, this is fine; if not, you may want a stop hook or a weak/indirection pattern.


56-61: Governance sync request wiring is straightforward

SendGovernanceSyncRequest’s construction of MNGOVERNANCESYNC with an empty Bloom filter and a zero hash matches the expectations in ProcessMessage and keeps the wire encoding simple. No issues here.


63-152: Vote request throttling and per‑hash/per‑peer limits look consistent

The logic in RequestGovernanceObjectVotes (per‑hash peer caps, projected vote count limit per peer, MAX_INV_SZ guard, and static mapAskedRecently with a mutex) is coherent and appears to preserve the intended behavior of avoiding bandwidth explosions while still stressing non‑main networks more heavily.

Assuming FetchGovernanceObjectVotes is responsible for aging out mapAskedRecently entries based on nNow and nTimeout, this design should remain bounded over long uptimes. If that assumption changed in the refactor, it’s worth confirming to avoid unbounded growth of mapAskedRecently.


345-449: ProcessMessage refactor preserves structure; minor duplication only

The three branches for MNGOVERNANCESYNC, MNGOVERNANCEOBJECT, and MNGOVERNANCEOBJECTVOTE look like a direct lift from the old governance message handler, with the right hand‑offs to CGovernanceManager and PeerManager and appropriate sync gating and misbehavior scoring.

There is some duplicated IsBlockchainSynced() checking (top of function plus inside the object/vote branches), but that’s harmless and may even be desirable given the concurrency between sync and net threads. No changes needed here.

src/net_processing.cpp (3)

652-652: NetHandler scheduling hook looks sound

ScheduleHandlers cleanly iterates m_handlers and delegates to handler->Schedule(scheduler, m_connman), matching the new interface and keeping scheduling centralized in PeerManagerImpl. No locking is needed as long as m_handlers is configured only during init/shutdown, which matches existing AddExtraHandler/RemoveHandlers usage.

Also applies to: 1701-1706


661-661: Locking contract for PeerGetRequestedObjectCount now matches internal state access

Requiring both !m_peer_mutex and ::cs_main on PeerGetRequestedObjectCount aligns the wrapper with GetRequestedObjectCount and the m_node_states (GUARDED_BY(cs_main)) access. The wrapper’s implementation is a straight passthrough, which is correct given the updated precondition; callers just need to ensure they now hold cs_main when using the peer-facing variant.

Also applies to: 1650-1659, 6574-6577


5461-5463: Passing CConnman& into NetHandlers is appropriate

Updating the NetHandler dispatch to handler->ProcessMessage(pfrom, m_connman, msg_type, vRecv); matches the expanded handler API and keeps all P2P sends/connection operations going through the shared CConnman instance. This is consistent with the refactoring goal of separating network orchestration from handler logic.

src/governance/governance.h (1)

367-388: Well-structured API boundary for the network/consensus separation.

The new "Used by NetGovernance" section clearly delineates the methods needed by the new NetGovernance handler. Lock annotations are consistently applied:

  • cs_relay for relay inventory operations
  • cs_store for governance object/vote storage operations
  • cs_main appropriately added to ProcessObject for chain-state access

The design correctly exposes data-fetching methods (FetchRelayInventory, FetchGovernanceObjectVotes) and processing entry points (ProcessObject, ProcessVote) that allow NetGovernance to handle network messaging while CGovernanceManager focuses on consensus logic.

GetMNManager() (line 385) returning a reference without lock annotations is correct since m_dmnman is a const member initialized at construction.

Comment on lines +246 to +257
if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) {
// Now that the blockchain is synced request the mempool from the connected outbound nodes if possible
for (auto pNodeTmp : snap.Nodes()) {
bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr,
"mempool-sync");
if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) {
m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync");
connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
LogPrint(BCLog::MNSYNC, /* Continued */
"Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick,
asset_id, pNodeTmp->GetId());
}
Copy link

@coderabbitai coderabbitai bot Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

BUG: MEMPOOL sync uses CNetMsgMaker from the wrong peer

In the mempool sync block you send MEMPOOL to pNodeTmp using a CNetMsgMaker constructed from pnode->GetCommonVersion():

CNetMsgMaker msgMaker(pnode->GetCommonVersion());
// ...
for (auto pNodeTmp : snap.Nodes()) {
    // ...
    connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
}

CNetMsgMaker bakes in the per‑peer protocol version; reusing a maker built for one peer when sending to another is incorrect and can lead to mismatched serialization if peers negotiate different versions.

You should construct the message maker using pNodeTmp’s negotiated version instead:

-                        for (auto pNodeTmp : snap.Nodes()) {
+                        for (auto pNodeTmp : snap.Nodes()) {
                             bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr,
                                                                                            "mempool-sync");
                             if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) {
                                 m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync");
-                                connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
+                                CNetMsgMaker mempoolMsgMaker(pNodeTmp->GetCommonVersion());
+                                connman.PushMessage(pNodeTmp, mempoolMsgMaker.Make(NetMsgType::MEMPOOL));
                                 LogPrint(BCLog::MNSYNC, /* Continued */
                                          "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick,
                                          asset_id, pNodeTmp->GetId());
                             }
                         }

This keeps behavior aligned with the rest of the codebase, where messages are always serialized with the destination peer’s own common version.

🤖 Prompt for AI Agents
In src/governance/net_governance.cpp around lines 246-257, the MEMPOOL message
is being created with a CNetMsgMaker built from the wrong peer (pnode) which
bakes in an incorrect protocol version; fix it by constructing the CNetMsgMaker
with the destination peer's negotiated version (pNodeTmp->GetCommonVersion())
when sending inside the loop (i.e. create a msgMaker per pNodeTmp or call
CNetMsgMaker(pNodeTmp->GetCommonVersion()) before connman.PushMessage), so each
MEMPOOL message is serialized using the target peer's common version.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be a real issue because all nodes >= MIN_PEER_PROTO_VERSION should support mempool message but I guess it makes sense to refactor it anyway. Could be done in a separate PR though.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@UdjinM6, that makes sense—since all nodes at or above MIN_PEER_PROTO_VERSION support the mempool message, the practical risk is minimal. Keeping this PR focused on the network/consensus separation refactoring is the right call.

If you'd like, I can open a follow-up issue to track this refactoring for a future PR, so it doesn't get lost. Just let me know!


🧠 Learnings used
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: The file src/masternode/sync.h containing `CMasternodeSync` is misnamed and misplaced—it has nothing to do with "masternode" functionality. It should eventually be renamed to `NodeSyncing` or `NodeSyncStatus` and moved to src/node/sync.h as a future refactoring.

Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: In Dash Core, `DEFAULT_SYNC_MEMPOOL` belongs with node synchronization logic (currently in src/masternode/sync.h) rather than with validation logic in src/validation.h, even though other mempool-related constants are in validation.h. The constant is conceptually tied to sync functionality, not validation.

Learnt from: kwvg
Repo: dashpay/dash PR: 6665
File: src/evo/providertx.h:82-82
Timestamp: 2025-06-06T11:53:09.094Z
Learning: In ProTx serialization code (SERIALIZE_METHODS), version checks should use hardcoded maximum flags (/*is_basic_scheme_active=*/true, /*is_extended_addr=*/true) rather than deployment-based flags. This is because serialization code should be able to deserialize any structurally valid ProTx up to the maximum version the code knows how to handle, regardless of current consensus validity. Validation code, not serialization code, is responsible for checking whether a ProTx version is consensus-valid based on deployment status.

Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.

Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.

Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/wallet/wallet.cpp:0-0
Timestamp: 2025-07-29T14:33:01.040Z
Learning: In refactoring PRs like #6761, kwvg acknowledges code safety improvements (like null pointer checks and unused parameter warnings) but prefers to defer them to follow-up PRs to maintain focus on the primary refactoring objectives, avoiding scope creep.

void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void PeerAskPeersForTransaction(const uint256& txid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
size_t PeerGetRequestedObjectCount(NodeId nodeid) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, ::cs_main);
void PeerPostProcessMessage(MessageProcessingResult&& ret) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

PeerPostProcessMessage drops peer context, disabling misbehavior and request cleanup

PeerPostProcessMessage currently forwards to:

void PeerManagerImpl::PeerPostProcessMessage(MessageProcessingResult&& ret)
{
    PostProcessMessage(std::move(ret), -1);
}

PostProcessMessage uses the node argument to:

  • Attribute m_error to a specific peer via Misbehaving(node, ...).
  • Call EraseObjectRequest(node, ...) under cs_main.

Passing -1 means any m_error or m_to_erase set by callers of PeerPostProcessMessage will be silently ignored: Misbehaving(-1, ...) and EraseObjectRequest(-1, ...) won’t find a peer/state entry. Only m_transactions, m_inventory, and m_dsq are effective.

If NetHandlers are expected to use PeerPostProcessMessage with a real per-peer MessageProcessingResult (which the name strongly suggests), this will undercut DoS accounting and object-request bookkeeping for those subsystems.

Consider tightening this by threading the actual NodeId through the interface, e.g.:

-void PeerPostProcessMessage(MessageProcessingResult&& ret) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+void PeerPostProcessMessage(MessageProcessingResult&& ret, NodeId node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);

and

-void PeerManagerImpl::PeerPostProcessMessage(MessageProcessingResult&& ret)
-{
-    PostProcessMessage(std::move(ret), -1);
-}
+void PeerManagerImpl::PeerPostProcessMessage(MessageProcessingResult&& ret, NodeId node)
+{
+    PostProcessMessage(std::move(ret), node);
+}

with corresponding updates to PeerManagerInternal and NetGovernance (or other) call sites so they pass pfrom.GetId() when there is a peer context, and only use -1 explicitly in the rare cases where there truly is none.

Also applies to: 3587-3604, 6579-6582

🤖 Prompt for AI Agents
In src/net_processing.cpp around line 662 (and similarly at 3587-3604 and
6579-6582), PeerPostProcessMessage currently calls PostProcessMessage with -1
which drops peer context and prevents Misbehaving(...) and
EraseObjectRequest(...) from attributing errors/cleanup to the correct peer;
change the PeerPostProcessMessage signature to accept a NodeId (or int node) and
forward that node to PostProcessMessage instead of -1, update the
PeerManagerInternal and NetGovernance (and any other callers) to pass
pfrom.GetId() when a peer exists (or -1 only when there truly is no peer), and
update the override/declaration sites accordingly so m_error and m_to_erase are
handled against the correct peer state under cs_main.

@knst
Copy link
Collaborator Author

knst commented Nov 25, 2025

it conflicts with #6992 but conflicts are pretty trivial. Whatever which one will get merged first; 2nd will be rebased on the top.

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

This pull request has conflicts, please rebase.

knst added 13 commits December 4, 2025 03:32
It is used to show this warning:

    Transaction index can't be disabled with governance validation enabled. Either start with -disablegovernance command line switch or enable transaction index.

New message is shown in this situation now; behavior is changed by forcing disabling governance:

    Warning: You are starting with governance validation disabled.
…uestOrphanObjects

It helps to manage amount of public methods of CGovernanceManager
…relevant code to NetGovernance

Please note that circular dependency masternode/sync -> net -> masternode/sync existed for a long time,
it's not newly introduced
@knst knst force-pushed the refactor-peermanager-handlers-gov branch from a5ef0da to ff49ff9 Compare December 3, 2025 20:53
@UdjinM6
Copy link

UdjinM6 commented Dec 4, 2025

clang-tidy complains https://github.com/dashpay/dash/actions/runs/19908490232/job/57116628917?pr=7008#step:9:2525

@knst knst force-pushed the refactor-peermanager-handlers-gov branch from ff49ff9 to 4a1fdd9 Compare December 4, 2025 11:18
Copy link

@coderabbitai coderabbitai bot left a 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/governance/net_governance.cpp (1)

154-261: BUG: MEMPOOL uses CNetMsgMaker from the wrong peer when syncing mempool

In the mempool sync block, msgMaker is constructed once per outer pnode:

CNetMsgMaker msgMaker(pnode->GetCommonVersion());
...
for (auto pNodeTmp : snap.Nodes()) {
    ...
    connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
}

CNetMsgMaker bakes in the destination peer’s protocol version, so reusing a maker built for pnode when sending to pNodeTmp is incorrect and can lead to version‑mismatched serialization when peers negotiated different protocol versions.

You should construct the message maker from each destination peer’s GetCommonVersion() inside the inner loop.

-        for (auto pNodeTmp : snap.Nodes()) {
+        for (auto pNodeTmp : snap.Nodes()) {
             bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr,
                                                                            "mempool-sync");
             if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) {
                 m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync");
-                connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
+                CNetMsgMaker mempoolMsgMaker(pNodeTmp->GetCommonVersion());
+                connman.PushMessage(pNodeTmp, mempoolMsgMaker.Make(NetMsgType::MEMPOOL));
                 LogPrint(BCLog::MNSYNC, /* Continued */
                          "Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick,
                          asset_id, pNodeTmp->GetId());
             }
         }
🧹 Nitpick comments (4)
src/governance/net_governance.cpp (1)

22-54: Governance scheduling via CScheduler is aligned with new handler model

The three scheduleEvery registrations cleanly separate: (1) 1s sync tick, (2) 5‑minute orphan/cleanup once fully synced, and (3) periodic relay of governance inventory. Using m_gov_manager.IsValid() to gate the heavier tasks avoids touching uninitialized governance state.

If you want to minimize work during shutdown, you could also short‑circuit the second and third lambdas on ShutdownRequested() similarly to the first.

src/governance/governance.cpp (2)

183-231: ProcessObject correctly encapsulates object validation; consider making cs_main requirement explicit

CGovernanceManager::ProcessObject:

  • Short‑circuits if the object hash is already known (including erased/postponed).
  • Runs MasternodeRateCheck (with the two‑phase “bypass then force” check) under cs_store.
  • Validates the object locally with the current MN list and chainstate, deferring immature fee cases into mapPostponedObjects.
  • Calls AddGovernanceObjectInternal(govobj, &peer) on success, which requires both ::cs_main and cs_store.

NetGovernance currently calls this under WITH_LOCK(::cs_main, ...), satisfying the locking precondition. To make this contract obvious and prevent future misuse, it would be good to add an AssertLockHeld(::cs_main); at the top of ProcessObject (or call AddGovernanceObject(...) instead of the internal helper).


960-991: Governance vote fetch API cleanly supports NetGovernance scheduling

FetchGovernanceObjectVotes(...):

  • Runs under cs_store, iterating mapObjects.
  • Skips deleted objects and those that already hit the nPeersPerHashMax limit in mapAskedRecently, pruning stale entries first.
  • Partitions hashes into triggers vs. other objects for prioritized querying.

This matches what NetGovernance::RequestGovernanceObjectVotes expects and keeps selection logic local to the governance manager.

You may want to document in the header that callers must hold their own mutex protecting mapAskedRecently (as NetGovernance does with cs_recently), since it is mutated inside this method.

src/net_processing.cpp (1)

641-648: Centralized handler scheduling via ScheduleHandlers looks good

The new ScheduleHandlers(CScheduler&) override and its implementation that iterates m_handlers and calls handler->Schedule(scheduler, m_connman) cleanly wires NetHandlers into the shared scheduler and exposes CConnman& as intended. Since m_handlers is also mutated without locking in AddExtraHandler/RemoveHandlers, this relies on lifecycle discipline (no concurrent mutation while scheduling); if there’s any chance of runtime additions/removals later, consider documenting that assumption or guarding m_handlers with a mutex as a future refactor.

Also applies to: 1696-1701

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5ef0da and 4a1fdd9.

📒 Files selected for processing (19)
  • src/Makefile.am (2 hunks)
  • src/governance/governance.cpp (9 hunks)
  • src/governance/governance.h (1 hunks)
  • src/governance/net_governance.cpp (1 hunks)
  • src/governance/net_governance.h (1 hunks)
  • src/init.cpp (3 hunks)
  • src/instantsend/net_instantsend.cpp (1 hunks)
  • src/instantsend/net_instantsend.h (1 hunks)
  • src/llmq/net_signing.cpp (1 hunks)
  • src/llmq/net_signing.h (1 hunks)
  • src/masternode/sync.cpp (1 hunks)
  • src/masternode/sync.h (2 hunks)
  • src/net_processing.cpp (5 hunks)
  • src/net_processing.h (3 hunks)
  • src/node/chainstate.cpp (0 hunks)
  • src/node/chainstate.h (0 hunks)
  • src/test/util/setup_common.cpp (0 hunks)
  • src/validation.h (0 hunks)
  • test/lint/lint-circular-dependencies.py (1 hunks)
💤 Files with no reviewable changes (4)
  • src/test/util/setup_common.cpp
  • src/node/chainstate.h
  • src/node/chainstate.cpp
  • src/validation.h
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/governance/net_governance.h
  • src/Makefile.am
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.{cpp,h,hpp,cc}

📄 CodeRabbit inference engine (CLAUDE.md)

Dash Core implementation must be written in C++20, requiring at least Clang 16 or GCC 11.1

Files:

  • src/instantsend/net_instantsend.h
  • src/llmq/net_signing.h
  • src/net_processing.h
  • src/init.cpp
  • src/governance/net_governance.cpp
  • src/llmq/net_signing.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
  • src/instantsend/net_instantsend.cpp
  • src/governance/governance.h
  • src/net_processing.cpp
src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Files:

  • src/llmq/net_signing.h
  • src/governance/net_governance.cpp
  • src/llmq/net_signing.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
  • src/governance/governance.h
src/{masternode,llmq}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

BLS integration must be used for cryptographic foundation of advanced masternode features

Files:

  • src/llmq/net_signing.h
  • src/llmq/net_signing.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
src/llmq/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

src/llmq/**/*.{cpp,h}: LLMQ quorums must support multiple configurations (50/60, 400/60, 400/85) for different services (ChainLocks, InstantSend, governance voting)
InstantSend implementation must provide distributed key generation for secure transaction locking with quorum consensus
ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention

Files:

  • src/llmq/net_signing.h
  • src/llmq/net_signing.cpp
src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Files:

  • src/llmq/net_signing.h
  • src/governance/net_governance.cpp
  • src/llmq/net_signing.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
  • src/governance/governance.h
src/governance/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Files:

  • src/governance/net_governance.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
src/{masternode,evo}/**/*.{cpp,h}

📄 CodeRabbit inference engine (CLAUDE.md)

Masternode lists must use immutable data structures (Immer library) for thread safety

Files:

  • src/masternode/sync.cpp
  • src/masternode/sync.h
🧠 Learnings (31)
📓 Common learnings
Learnt from: kwvg
Repo: dashpay/dash PR: 6543
File: src/wallet/receive.cpp:240-251
Timestamp: 2025-02-06T14:34:30.466Z
Learning: Pull request #6543 is focused on move-only changes and refactoring, specifically backporting from Bitcoin. Behavior changes should be proposed in separate PRs.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/wallet/wallet.cpp:0-0
Timestamp: 2025-07-29T14:33:01.040Z
Learning: In refactoring PRs like #6761, kwvg acknowledges code safety improvements (like null pointer checks and unused parameter warnings) but prefers to defer them to follow-up PRs to maintain focus on the primary refactoring objectives, avoiding scope creep.
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,consensus,net_processing}/**/*.{cpp,h} : ValidationInterface callbacks must be used for event-driven architecture to coordinate subsystems during block/transaction processing

Applied to files:

  • src/net_processing.h
  • src/init.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/governance/**/*.{cpp,h} : Governance implementation must support governance objects (proposals, triggers, superblock management) and on-chain voting with tallying

Applied to files:

  • src/init.cpp
  • src/governance/net_governance.cpp
  • src/masternode/sync.cpp
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/**/*.{cpp,h} : Special transactions use payload serialization routines defined in src/evo/specialtx.h and must include appropriate special transaction types (ProRegTx, ProUpServTx, ProUpRegTx, ProUpRevTx)

Applied to files:

  • src/init.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/evo/evodb/**/*.{cpp,h} : Evolution Database (CEvoDb) must handle masternode snapshots, quorum state, governance objects with efficient differential updates for masternode lists

Applied to files:

  • src/init.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,evo,llmq,governance,coinjoin}/**/*.{cpp,h} : Use Dash-specific database implementations: CFlatDB for persistent storage (MasternodeMetaStore, GovernanceStore, SporkStore, NetFulfilledRequestStore) and CDBWrapper extensions for Evolution/DKG/InstantSend/Quorum/RecoveredSigs data

Applied to files:

  • src/init.cpp
  • src/masternode/sync.h
  • src/governance/governance.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/llmq/**/*.{cpp,h} : InstantSend implementation must provide distributed key generation for secure transaction locking with quorum consensus

Applied to files:

  • src/init.cpp
📚 Learning: 2025-10-28T08:54:00.392Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6926
File: test/functional/feature_governance_cl.py:0-0
Timestamp: 2025-10-28T08:54:00.392Z
Learning: In Dash tests, the scheduler (mockscheduler) operates independently of network state. Isolated nodes should still run scheduler-based cleanup processes like governance cleanup, even if they have different state due to network isolation.

Applied to files:

  • src/init.cpp
  • src/governance/governance.cpp
📚 Learning: 2025-11-25T10:53:37.523Z
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: The file src/masternode/sync.h containing `CMasternodeSync` is misnamed and misplaced—it has nothing to do with "masternode" functionality. It should eventually be renamed to `NodeSyncing` or `NodeSyncStatus` and moved to src/node/sync.h as a future refactoring.

Applied to files:

  • src/governance/net_governance.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
📚 Learning: 2025-11-25T10:53:37.523Z
Learnt from: knst
Repo: dashpay/dash PR: 7008
File: src/masternode/sync.h:17-18
Timestamp: 2025-11-25T10:53:37.523Z
Learning: In Dash Core, `DEFAULT_SYNC_MEMPOOL` belongs with node synchronization logic (currently in src/masternode/sync.h) rather than with validation logic in src/validation.h, even though other mempool-related constants are in validation.h. The constant is conceptually tied to sync functionality, not validation.

Applied to files:

  • src/governance/net_governance.cpp
  • src/masternode/sync.cpp
  • src/masternode/sync.h
  • src/governance/governance.cpp
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket access operations (m_sock and m_server) should be protected by the cs_net mutex, not avoiding synchronization entirely. While lock overhead concerns are valid in general, socket operations require proper synchronization via cs_net.

Applied to files:

  • src/governance/net_governance.cpp
  • src/instantsend/net_instantsend.cpp
  • src/net_processing.cpp
📚 Learning: 2025-06-06T11:53:09.094Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6665
File: src/evo/providertx.h:82-82
Timestamp: 2025-06-06T11:53:09.094Z
Learning: In ProTx serialization code (SERIALIZE_METHODS), version checks should use hardcoded maximum flags (/*is_basic_scheme_active=*/true, /*is_extended_addr=*/true) rather than deployment-based flags. This is because serialization code should be able to deserialize any structurally valid ProTx up to the maximum version the code knows how to handle, regardless of current consensus validity. Validation code, not serialization code, is responsible for checking whether a ProTx version is consensus-valid based on deployment status.

Applied to files:

  • src/governance/net_governance.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{validation,txmempool}/**/*.{cpp,h} : Block validation and mempool handling must use extensions to Bitcoin Core mechanisms for special transaction validation and enhanced transaction relay

Applied to files:

  • src/masternode/sync.h
  • src/governance/governance.cpp
📚 Learning: 2025-11-04T18:24:27.241Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/llmq/utils.cpp:284-298
Timestamp: 2025-11-04T18:24:27.241Z
Learning: In consensus-critical code (such as quorum formation, block validation, or deployment activation logic), do not suggest changes to the logic itself even if the implementation appears theoretically incorrect or off-by-one. Consensus rules, once deployed on the Dash network, must be preserved exactly to avoid network forks. Refactoring PRs should maintain perfect behavioral equivalence. Only suggest logic changes if explicitly accompanied by a DIP (Dash Improvement Proposal) or if the maintainer indicates the consensus rule needs to be changed with appropriate activation logic.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-06-09T16:43:20.996Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6718
File: test/functional/test_framework/test_framework.py:2102-2102
Timestamp: 2025-06-09T16:43:20.996Z
Learning: In the test framework consolidation PR (#6718), user kwvg prefers to limit functional changes to those directly related to MasternodeInfo, avoiding scope creep even for minor improvements like error handling consistency.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-10-05T20:38:28.457Z
Learnt from: knst
Repo: dashpay/dash PR: 6871
File: contrib/guix/libexec/build.sh:358-360
Timestamp: 2025-10-05T20:38:28.457Z
Learning: In the Dash repository, when backporting code from Bitcoin Core, typos and minor issues in comments should be kept as-is to reduce merge conflicts in future backports, even if they remain unfixed in Bitcoin Core's master branch.

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-07-29T14:32:48.369Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/chainlock/signing.cpp:247-250
Timestamp: 2025-07-29T14:32:48.369Z
Learning: In PR #6761, kwvg acknowledged a null pointer check issue in ChainLockSigner::Cleanup() method but deferred it to follow-up, consistent with the pattern of avoiding scope creep in refactoring PRs.

Applied to files:

  • src/masternode/sync.h
  • src/net_processing.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq,evo,coinjoin,governance}/**/*.{cpp,h} : Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/{masternode,llmq}/**/*.{cpp,h} : BLS integration must be used for cryptographic foundation of advanced masternode features

Applied to files:

  • src/masternode/sync.h
  • src/governance/governance.cpp
📚 Learning: 2025-11-24T16:41:22.457Z
Learnt from: CR
Repo: dashpay/dash PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:41:22.457Z
Learning: Applies to src/coinjoin/**/*.{cpp,h} : CoinJoin implementation must use masternode-coordinated mixing sessions with uniform denomination outputs

Applied to files:

  • src/masternode/sync.h
📚 Learning: 2025-11-04T18:23:28.175Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6933
File: src/governance/classes.cpp:147-154
Timestamp: 2025-11-04T18:23:28.175Z
Learning: In src/governance/classes.cpp, CSuperblock::GetPaymentsLimit intentionally uses synthetic difficulty constants (nBits = 1 for mainnet, powLimit for networks with min difficulty) and simple height-based V20 activation checks instead of actual chain block data. This is by design because superblocks themselves are "synthetic" governance payment blocks, not regular mined blocks.

Applied to files:

  • src/masternode/sync.h
  • src/governance/governance.h
📚 Learning: 2025-10-21T11:09:34.688Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6849
File: src/governance/governance.cpp:1339-1343
Timestamp: 2025-10-21T11:09:34.688Z
Learning: In PR #6849, `cmapVoteToObject` was intentionally moved from `GovernanceStore` to `CGovernanceManager` as a memory-only variable and is NOT guarded by `cs_store`. It relies on `CacheMap`'s internal thread-safety instead.

Applied to files:

  • src/governance/governance.cpp
  • src/governance/governance.h
📚 Learning: 2025-09-09T21:36:58.969Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:223-232
Timestamp: 2025-09-09T21:36:58.969Z
Learning: In RawSender class (src/stats/rawsender.cpp), socket operations are properly synchronized using the cs_net mutex. The m_sock and m_server members are GUARDED_BY(cs_net), and methods like Connect(), SendDirectly(), and ReconnectThread() use appropriate locking with cs_net to prevent race conditions on socket access.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access requires synchronization using the cs_net mutex. The race condition exists between SendDirectly() (callable from any thread) and Connect() (called by ReconnectThread), where both read and write m_sock concurrently. The cs_net mutex properly coordinates these network operations.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks. The implementation correctly uses cs_net with GUARDED_BY annotations and EXCLUSIVE_LOCKS_REQUIRED to synchronize socket access between SendDirectly() and ReconnectThread().

Applied to files:

  • src/instantsend/net_instantsend.cpp
  • src/governance/governance.h
  • src/net_processing.cpp
📚 Learning: 2025-09-09T21:36:31.504Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:31.504Z
Learning: In RawSender class (src/stats/rawsender.cpp), m_sock access is protected by the cs_net mutex. This mutex coordinates between the TCP send path in SendDirectly() and the reconnection operations in Connect()/Reconnect() methods, ensuring proper synchronization of socket state.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-09-09T21:36:11.833Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6837
File: src/stats/rawsender.cpp:0-0
Timestamp: 2025-09-09T21:36:11.833Z
Learning: In RawSender class (src/stats/rawsender.cpp), cs_net is the appropriate mutex for protecting socket access (m_sock) and network operations, not additional custom locks.

Applied to files:

  • src/instantsend/net_instantsend.cpp
📚 Learning: 2025-11-13T20:02:55.480Z
Learnt from: UdjinM6
Repo: dashpay/dash PR: 6969
File: src/evo/deterministicmns.h:441-479
Timestamp: 2025-11-13T20:02:55.480Z
Learning: In `src/evo/deterministicmns.h`, the `internalId` field in `CDeterministicMN` and the `mnInternalIdMap` in `CDeterministicMNList` are non-deterministic and used only for internal bookkeeping and efficient lookups. Different nodes can assign different internalIds to the same masternode depending on their sync history. Methods like `IsEqual()` intentionally ignore internalId mappings and only compare consensus-critical deterministic fields (proTxHash, collateral, state, etc.).

Applied to files:

  • src/governance/governance.h
📚 Learning: 2025-08-19T14:57:31.801Z
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/blockprocessor.cpp:217-224
Timestamp: 2025-08-19T14:57:31.801Z
Learning: In PR #6692, knst acknowledged a null pointer dereference issue in ProcessBlock() method where LookupBlockIndex may return nullptr but is passed to gsl::not_null, and created follow-up PR #6789 to address it, consistent with avoiding scope creep in performance-focused PRs.

Applied to files:

  • src/net_processing.cpp
📚 Learning: 2025-07-29T14:33:01.040Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6761
File: src/wallet/wallet.cpp:0-0
Timestamp: 2025-07-29T14:33:01.040Z
Learning: In refactoring PRs like #6761, kwvg acknowledges code safety improvements (like null pointer checks and unused parameter warnings) but prefers to defer them to follow-up PRs to maintain focus on the primary refactoring objectives, avoiding scope creep.

Applied to files:

  • src/net_processing.cpp
🧬 Code graph analysis (7)
src/instantsend/net_instantsend.h (2)
src/instantsend/net_instantsend.cpp (2)
  • ProcessMessage (17-65)
  • ProcessMessage (17-17)
src/llmq/net_signing.cpp (2)
  • ProcessMessage (19-35)
  • ProcessMessage (19-19)
src/llmq/net_signing.h (2)
src/net_processing.cpp (12)
  • ProcessMessage (3687-5477)
  • ProcessMessage (3687-3692)
  • pfrom (613-613)
  • pfrom (634-635)
  • pfrom (748-750)
  • pfrom (759-759)
  • pfrom (767-767)
  • pfrom (770-770)
  • pfrom (772-772)
  • pfrom (774-774)
  • pfrom (870-870)
  • pfrom (1052-1052)
src/llmq/net_signing.cpp (2)
  • ProcessMessage (19-35)
  • ProcessMessage (19-19)
src/net_processing.h (1)
src/net_processing.cpp (20)
  • PeerGetRequestedObjectCount (6580-6583)
  • PeerGetRequestedObjectCount (6580-6580)
  • nodeid (623-623)
  • nodeid (626-626)
  • nodeid (639-639)
  • nodeid (651-651)
  • nodeid (656-656)
  • nodeid (674-674)
  • nodeid (676-676)
  • nodeid (714-715)
  • nodeid (725-725)
  • nodeid (857-857)
  • nodeid (859-859)
  • nodeid (1035-1035)
  • nodeid (1070-1070)
  • scheduler (619-619)
  • scheduler (647-647)
  • scheduler (689-689)
  • ScheduleHandlers (1696-1701)
  • ScheduleHandlers (1696-1696)
src/governance/net_governance.cpp (2)
src/net_processing.cpp (3)
  • scheduler (619-619)
  • scheduler (647-647)
  • scheduler (689-689)
src/net.h (1)
  • FullyConnectedOnly (1296-1296)
src/masternode/sync.h (1)
src/masternode/sync.cpp (6)
  • BumpAssetLastTime (43-48)
  • BumpAssetLastTime (43-43)
  • GetAssetName (50-59)
  • GetAssetName (50-50)
  • GetSyncStatus (89-97)
  • GetSyncStatus (89-89)
src/instantsend/net_instantsend.cpp (2)
src/net_processing.cpp (12)
  • ProcessMessage (3687-5477)
  • ProcessMessage (3687-3692)
  • pfrom (613-613)
  • pfrom (634-635)
  • pfrom (748-750)
  • pfrom (759-759)
  • pfrom (767-767)
  • pfrom (770-770)
  • pfrom (772-772)
  • pfrom (774-774)
  • pfrom (870-870)
  • pfrom (1052-1052)
src/llmq/net_signing.cpp (2)
  • ProcessMessage (19-35)
  • ProcessMessage (19-19)
src/governance/governance.h (2)
src/governance/governance.cpp (22)
  • FetchRelayInventory (449-455)
  • FetchRelayInventory (449-449)
  • CheckAndRemove (332-447)
  • CheckAndRemove (332-332)
  • RequestOrphanObjects (1169-1208)
  • RequestOrphanObjects (1169-1169)
  • FetchGovernanceObjectVotes (962-991)
  • FetchGovernanceObjectVotes (962-963)
  • RequestGovernanceObject (928-958)
  • RequestGovernanceObject (928-928)
  • SyncObjects (650-692)
  • SyncObjects (650-650)
  • SyncSingleObjVotes (597-648)
  • SyncSingleObjVotes (597-597)
  • AcceptMessage (993-1005)
  • AcceptMessage (993-993)
  • ProcessObject (183-231)
  • ProcessObject (183-183)
  • GetMNManager (960-960)
  • GetMNManager (960-960)
  • ProcessVote (800-858)
  • ProcessVote (800-800)
src/governance/object.cpp (2)
  • ProcessVote (58-159)
  • ProcessVote (58-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
🔇 Additional comments (25)
src/llmq/net_signing.cpp (1)

19-19: LGTM: ProcessMessage signature updated for API consistency.

The added CConnman& parameter aligns with the broader NetHandler interface harmonization across the codebase. The parameter is currently unused but available for future networking needs.

src/llmq/net_signing.h (1)

28-28: LGTM: Declaration matches implementation.

The signature update correctly reflects the implementation change in the corresponding .cpp file.

src/masternode/sync.h (1)

67-73: LGTM: New accessors support governance refactoring.

The added public methods (BumpAttempt(), GetLastBump(), IsReachedBestHeader()) appropriately expose internal sync state needed by the governance network handler, aligning with the PR's goal of separating network and consensus logic.

src/governance/governance.h (1)

367-387: LGTM: Well-structured governance interface for network separation.

The new public API surface clearly delineates methods used by NetGovernance, with comprehensive locking annotations (EXCLUSIVE_LOCKS_REQUIRED) that document synchronization requirements. This reorganization successfully separates network-facing operations from internal governance logic, advancing the PR's refactoring goals.

src/instantsend/net_instantsend.cpp (1)

17-17: LGTM: Consistent signature update across NetHandler implementations.

The CConnman& parameter addition aligns with the NetHandler interface standardization. While currently unused, it maintains API consistency and enables future networking functionality.

src/instantsend/net_instantsend.h (1)

34-34: LGTM: Declaration consistent with implementation.

The signature update correctly reflects the corresponding .cpp implementation and NetHandler interface requirements.

test/lint/lint-circular-dependencies.py (1)

51-51: LGTM: Simplified circular dependency structure.

The consolidation of multiple governance-related circular dependencies into a single masternode/sync -> net -> masternode/sync path demonstrates successful architectural improvement. This simpler dependency graph validates the refactoring's goal of better separation between network and consensus logic.

src/net_processing.h (3)

67-67: LGTM: New method supports handler decision-making.

The PeerGetRequestedObjectCount() method appropriately exposes per-peer request state, enabling NetGovernance and other handlers to make informed sync decisions based on peer activity.


83-84: LGTM: Handler interface enhancements enable unified scheduling.

The addition of Schedule() and the updated ProcessMessage() signature establish a consistent pattern for handlers to register periodic tasks and access connection management. This standardization supports the modular handler architecture introduced in this refactoring.


174-174: LGTM: Central scheduling coordination point.

The ScheduleHandlers() method provides a clean entry point for initializing all registered handlers' scheduled tasks, maintaining architectural consistency across the handler subsystem.

src/masternode/sync.cpp (1)

7-8: Includes correctly narrowed to chain/net types actually used

Adding chain.h and net.h matches the CBlockIndex and CNode/CConnman usages in this file and helps decouple from higher-level headers. No behavior change here.

src/init.cpp (3)

90-92: Governance net include and -syncmempool wiring look consistent

Including governance/net_governance.h here and using DEFAULT_SYNC_MEMPOOL for the -syncmempool option keeps init’s dependency surface minimal while placing the mempool-sync default next to node-syncing logic (in masternode/sync.h). No issues from a wiring/ownership standpoint.

Also applies to: 578-578


1979-2031: Updated LoadChainstate call matches the new consensus/node wiring

The new LoadChainstate(...) invocation passes CGovernanceManager, masternode meta, sync, spork, evo, and mempool objects without the old is_governance_enabled/-txindex/NetworkIDString() parameters. Given is_governance_enabled still only gates govman->LoadCache(...), this preserves behavior while simplifying the chainstate API surface.


2233-2234: NetGovernance handler integration into PeerManager/scheduler is coherent

NetGovernance is added as an extra handler, then all handlers are scheduled via peerman->ScheduleHandlers(*node.scheduler) and later started with peerman->StartScheduledTasks(*node.scheduler). This keeps governance networking aligned with the generic handler scheduling model and respects object lifetimes (handlers scheduled before RPC warmup ends, scheduler stopped before peerman/govman are reset).

Also applies to: 2302-2306, 2680-2681

src/governance/net_governance.cpp (4)

56-61: Governance sync request helper correctly encapsulates per‑peer versioning

SendGovernanceSyncRequest reuses the legacy pattern of sending MNGOVERNANCESYNC with an empty filter, and crucially builds CNetMsgMaker from pnode->GetCommonVersion(), so serialization matches the target peer.


63-152: Vote request scheduling with per‑hash/per‑peer caps looks sound

RequestGovernanceObjectVotes:

  • Uses distinct caps for real networks vs. regtest (REALNET_PEERS_PER_HASH/REGTEST_PEERS_PER_HASH).
  • Computes nMaxObjRequestsPerNode based on projected votes and current MN count to stress non‑mainnet.
  • Maintains mapAskedRecently under a dedicated mutex and prunes expired entries.
  • Avoids MAX_INV_SZ overflow by consulting PeerGetRequestedObjectCount under cs_main.
  • Skips non‑relay and inbound MN connections.

This mirrors the previous logic while cleanly separating it into a handler‑local API.


265-343: Governance sync tick logic preserves the original asset‑transition behavior

The governance phase of ProcessTick:

  • Skips governance entirely if m_gov_manager.IsValid() is false, moving straight to MASTERNODE_SYNC_FINISHED.
  • Enforces MASTERNODE_SYNC_TIMEOUT_SECONDS with the existing logging and fallback behavior.
  • Marks each peer with "governance-sync" only once and then sends a sync request via SendGovernanceSyncRequest.
  • After all peers are marked, performs per‑peer per‑object vote requests via RequestGovernanceObjectVotes and uses the historic nTimeNoObjectsLeft/nLastVotes heuristic to decide when governance sync is complete.

This matches prior semantics while routing all network IO through NetGovernance.


345-449: Message dispatch cleanly separated into NetGovernance/CGovernanceManager layers

ProcessMessage:

  • Immediately bails out if governance is disabled (!m_gov_manager.IsValid()) or the blockchain is not yet synced.
  • For MNGOVERNANCESYNC, defers to CGovernanceManager::SyncObjects / SyncSingleObjVotes and uses PeerPostProcessMessage to send the resulting inventories plus a SYNCSTATUSCOUNT.
  • For MNGOVERNANCEOBJECT, erases the outstanding request under cs_main, then calls CGovernanceManager::ProcessObject under WITH_LOCK(cs_main, ...), satisfying its need for cs_main/cs_store and applying a ban score on hard failure.
  • For MNGOVERNANCEOBJECTVOTE, erases the outstanding request, checks sync, logs with the tip MN list, enforces AcceptMessage, and then calls ProcessVote; on success it bumps the sync timer and relays only if fully synced.

Threading and ownership expectations line up with the new governance APIs.

src/governance/governance.cpp (5)

27-27: Switch to validationinterface.h keeps governance decoupled from validation internals

Including validationinterface.h instead of validation.h is appropriate here: this file only needs the notification interface (GetMainSignals()), not the full validation machinery, which helps reduce coupling.


449-455: Relay inventory fetch/clear under cs_relay is straightforward

FetchRelayInventory() swaps m_relay_invs into a local vector under cs_relay, providing a cheap, race‑free way for NetGovernance to drain pending relay items. This is a clean separation of producer (RelayObject/RelayVote) and consumer (NetGovernance::Schedule).


597-692: Adding cs_store locking to sync helpers tightens thread safety

Both SyncSingleObjVotes and SyncObjects now:

  • Lock cs_store while walking mapObjects and accessing per‑object vote files.
  • Still gate on m_mn_sync.IsSynced() and on m_netfulfilledman limits for full sync requests.
  • Use CNetMsgMaker(peer.GetCommonVersion()) to send SYNCSTATUSCOUNT with accurate per‑peer counts.

This improves consistency with other CGovernanceManager methods and avoids concurrent map access races.


1173-1208: Orphan vote cleanup before requesting parents is sensible and bounded

RequestOrphanObjects now:

  • Computes nNow in seconds and removes stale cmmapOrphanVotes entries whose stored expiry time is in the past.
  • Then builds vecHashesFiltered only for hashes not already in mapObjects.
  • Uses CConnman::NodesSnapshot with FullyConnectedOnly and only sends requests to peers that can relay.

This avoids repeatedly requesting already‑expired orphan parents and keeps the orphan map bounded over time.


960-960: GetMNManager accessor exposes deterministic MN manager for net layer

GetMNManager() returning *Assert(m_dmnman) is the right minimal API for NetGovernance to get the current deterministic MN list without exposing broader CGovernanceManager internals.

src/net_processing.cpp (2)

5467-5469: Passing CConnman& into NetHandlers’ ProcessMessage is consistent with the new API

Updating the dispatch loop to call handler->ProcessMessage(pfrom, m_connman, msg_type, vRecv) gives NetHandlers direct access to the connection manager and matches the refactored NetHandler interface, without changing control flow here. No additional issues from this change in ProcessMessage are apparent.


649-657: PeerGetRequestedObjectCount lock contract now matches GetRequestedObjectCount

Annotating PeerGetRequestedObjectCount with EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, ::cs_main) ensures the wrapper's precondition aligns with its callee. Since GetRequestedObjectCount accesses m_node_states (which is GUARDED_BY(cs_main)), callers must hold cs_main. Verify that all call sites of PeerGetRequestedObjectCount hold cs_main and respect the !m_peer_mutex ordering to prevent hitting the internal AssertLockHeld(cs_main) or racing on m_node_states.

@knst
Copy link
Collaborator Author

knst commented Dec 4, 2025

return int(vTriggerObjHashes.size() + vOtherObjHashes.size());
}

void NetGovernance::ProcessTick(CConnman& connman)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does more than just governance sync, I don't think it's the right place for it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider smth like 491f769

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does more than just governance sync, I don't think it's the right place for it.

Yes, I would agree that it's not the best name and place, but the previous caller has been CMasternodeSync::DoMaintenance; it has nothing to do with maintenance as well.

Also Governance should be aware if node is synced (IsBlockchainSynced, IsSynced), but this object CMasternodeSync also do a lot of calls from CGovernanceManager;

Probably better from "architecture" and logical point of view, create one more class and move CMasternodeSync's scheduler to there, but every new file -> bigger compilation time, so, each new file with non-trivial logic is an extra burden to support and to compile.


Should I still change it or that's non-actionable comment? @UdjinM6

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

working on, PR converted to draft

Comment on lines +246 to +257
if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) {
// Now that the blockchain is synced request the mempool from the connected outbound nodes if possible
for (auto pNodeTmp : snap.Nodes()) {
bool fRequestedEarlier = m_netfulfilledman.HasFulfilledRequest(pNodeTmp->addr,
"mempool-sync");
if (!pNodeTmp->IsInboundConn() && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) {
m_netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync");
connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL));
LogPrint(BCLog::MNSYNC, /* Continued */
"Sync Tick -- nTick %d asset_id %d -- syncing mempool from peer=%d\n", nTick,
asset_id, pNodeTmp->GetId());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be a real issue because all nodes >= MIN_PEER_PROTO_VERSION should support mempool message but I guess it makes sense to refactor it anyway. Could be done in a separate PR though.

UdjinM6 added a commit to UdjinM6/dash that referenced this pull request Dec 5, 2025
… coordinator pattern

Problem:
PR dashpay#7008 moved ProcessTick to NetGovernance, but ProcessTick handles more than
just governance synchronization - it also handles spork sync, blockchain sync,
and mempool sync. This creates a naming mismatch where "NetGovernance" contains
non-governance code.

Solution:
Implement coordinator pattern to properly separate concerns:

1. **SyncPhaseHandlerInterface** (sync_phase_handler.h)
   - Defines common interface for all sync phase handlers
   - ProcessPeer(): called for each peer during sync
   - PostPeerLoop(): called after processing all peers
   - OnSyncComplete(): called when phase completes

2. **NetNodeSyncCoordinator** (masternode/net_sync.{h,cpp})
   - Manages sync state machine
   - Delegates to phase-specific handlers
   - Supports always-active handlers (run during all phases)
   - Replaces monolithic ProcessTick logic

3. **NetSporkHandler** (spork/net_spork.{h,cpp})
   - Handles ONLY spork synchronization
   - Runs as always-active handler during all sync phases
   - ~8 lines of focused code

4. **NetBlockchainSyncHandler** (masternode/net_blockchain_sync.{h,cpp})
   - Handles ONLY blockchain + mempool synchronization
   - Runs during MASTERNODE_SYNC_BLOCKCHAIN phase
   - ~30 lines of focused code

5. **NetGovernance** (governance/net_governance.{h,cpp}) - refactored
   - Now handles ONLY governance synchronization
   - Implements SyncPhaseHandlerInterface for MASTERNODE_SYNC_GOVERNANCE phase
   - Removed spork, blockchain, and mempool sync code (~50 lines removed)
   - Maintains ProcessMessage for governance network messages
   - PostPeerLoop requests votes on per-object basis and detects completion

Benefits:
- Single Responsibility: Each handler has one clear purpose
- No Naming Mismatch: NetGovernance now only handles governance
- Better Testability: Handlers can be tested independently
- Cleaner Separation: Sporks, blockchain, and governance properly isolated
- Extensibility: Easy to add new sync phases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@knst knst marked this pull request as draft December 5, 2025 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants