Skip to content

Conversation

@PastaPastaPasta
Copy link
Member

Issue being fixed or feature implemented

Updated the GetMasternodeQuorumNodes method to return a vector of NodeId instead of an unordered_set. This change simplifies the logic for filtering and transforming nodes, enhancing readability and maintainability. Additionally, adjusted related code in ProcessSigShare to accommodate the new return type.

It's better to use a vector over a set, as all users are just iterating through this. They don't actually benefit from the O(1) lookup, so let's get the better benefits of cache locality.

What was done?

How Has This Been Tested?

Breaking Changes

Checklist:

Go over all the following points, and put an x in all the boxes that apply.

  • 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 (for repository code-owners and collaborators only)

…set to vector

Updated the GetMasternodeQuorumNodes method to return a vector of NodeId instead of an unordered_set. This change simplifies the logic for filtering and transforming nodes, enhancing readability and maintainability. Additionally, adjusted related code in ProcessSigShare to accommodate the new return type.
@github-actions
Copy link

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@coderabbitai
Copy link

coderabbitai bot commented Oct 16, 2025

Walkthrough

This pull request refactors the internal representation of quorum member node collections by replacing std::unordered_set<NodeId> with std::vector<NodeId> across multiple files. The public method CConnman::GetMasternodeQuorumNodes() return type changes accordingly, updating both the header declaration and implementation. Additionally, CSigSharesManager::ProcessSigShare() introduces local caching of the IsAllMembersConnectedEnabled() result to reduce repeated function calls. The masternode selection logic remains semantically equivalent, but the container type change alters deduplication behavior (sets guarantee uniqueness; vectors do not) and ordering characteristics.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Rationale: The changes involve a public API modification affecting multiple call sites, container type changes with different semantic implications (set → vector introduces potential duplicates and ordering changes), and cross-file dependencies. While the logic modifications are not densely complex, the reviewer must verify that removing implicit deduplication from the set data structure does not introduce unintended duplicates in downstream logic, and confirm that all callers of GetMasternodeQuorumNodes() handle the new vector return type correctly. The caching optimization in signing_shares.cpp is straightforward but requires tracing the impact of reduced function call frequency.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 pull request title "refactor: change GetMasternodeQuorumNodes return type from unordered_set to vector" is fully related to the main change in the changeset. It accurately and specifically describes the primary API modification across src/net.h, src/net.cpp, and related code in src/llmq/signing_shares.cpp. The title is concise, clear, and explicitly names the method being refactored and the nature of the change, allowing reviewers to immediately understand the core purpose of the PR.
Description Check ✅ Passed The pull request description is related to the changeset and explains the purpose of the refactoring. It identifies the specific method being modified (GetMasternodeQuorumNodes), describes the container type change from unordered_set to vector, and provides a clear rationale for the change (better cache locality since callers only iterate and do not benefit from O(1) lookup). The description also mentions that related code in ProcessSigShare was adjusted to accommodate the new return type, which aligns with the actual changes in the raw summary. While some sections (What was done, Testing, Breaking changes) are left empty and checklist items are unchecked, the core description is substantive and directly relevant to the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e60157a and f0b71d5.

📒 Files selected for processing (3)
  • src/llmq/signing_shares.cpp (2 hunks)
  • src/net.cpp (2 hunks)
  • src/net.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/llmq/signing_shares.cpp
  • src/net.h
  • src/net.cpp
🔇 Additional comments (5)
src/llmq/signing_shares.cpp (3)

720-720: LGTM! Caching optimization reduces redundant function calls.

Caching the result of IsAllMembersConnectedEnabled(llmqType, m_sporkman) is a good optimization that eliminates redundant calls at lines 724 and 738.


738-740: Consistent use of cached condition.

Correctly uses the cached isAllMembersConnectedEnabled variable instead of calling the function again, consistent with the optimization at line 720.


723-726: Verify that duplicate NodeIds cannot occur or are handled correctly.

The container type changed from std::unordered_set<NodeId> (which guarantees uniqueness) to std::vector<NodeId> (which allows duplicates). At lines 747-753, this vector is iterated to update node states. If GetMasternodeQuorumNodes() can return duplicate NodeIds, the same node would be processed multiple times, which is inefficient though the operations appear idempotent.

Run the following script to examine the implementation of GetMasternodeQuorumNodes and verify whether it can return duplicates:

src/net.h (1)

1488-1488: Verify all call sites handle the new return type correctly.

The public API return type changed from std::unordered_set<NodeId> to std::vector<NodeId>. Unlike sets, vectors can contain duplicates and have different performance characteristics (O(n) for lookups vs O(1) for sets, but better iteration performance due to cache locality).

Ensure that:

  1. All call sites have been updated to work with std::vector<NodeId>
  2. Call sites that require unique NodeIds either deduplicate the vector or verify the implementation guarantees uniqueness
  3. Call sites that performed lookups using the set's O(1) characteristics are updated appropriately

Run the following script to find and examine all call sites:

src/net.cpp (1)

4351-4374: Verify that duplicate NodeIds are acceptable at all call sites.

The change from std::unordered_set<NodeId> to std::vector<NodeId> removes automatic deduplication. While unlikely (since each CNode* in m_nodes should be unique), if the same node pointer somehow appears multiple times in m_nodes, the returned vector will now contain duplicate NodeId values, whereas the old set would have deduplicated them automatically.

The implementation correctly uses C++20 ranges/views for a clean, functional approach. The vector is appropriate for iteration-only usage and provides better cache locality.

Run the following script to verify how call sites handle the new return type and whether they expect uniqueness:

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK f0b71d5

@PastaPastaPasta PastaPastaPasta requested review from knst and kwvg October 21, 2025 14:34
Copy link
Collaborator

@kwvg kwvg left a comment

Choose a reason for hiding this comment

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

utACK f0b71d5

Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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

utACK f0b71d5

};

for (NodeId id : m_nodes
| std::views::filter(IsMasternodeQuorumNode)
Copy link
Collaborator

Choose a reason for hiding this comment

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

is std::views::filter & std::views::transform library or language feature?

I have concerns that this approach in long-term may make compilation time slower, if that's library feature; but I have no input at the moment.

Copy link
Member Author

Choose a reason for hiding this comment

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

@coderabbitai please investigate and respond to this question

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🌐 Web query:

C++20 ranges views compilation time impact performance

💡 Result:

Short answer: C++20 ranges/views commonly increase compile times (sometimes noticeably) but usually have no runtime cost if the compiler inlines and optimizes the pipeline; the trade-off is heavier template/headers and more work for the compiler. Suggested mitigations: limit exposure (compile boundaries/PCH), prefer MSVC or up-to-date toolchains, avoid overly-deep templated pipelines in hot build paths, and use type-erasure or hand-written loops where compile-time cost matters. [1][2][3][4]

Supporting points and sources

  • Compile-time impact: std::ranges and range-v3 are template‑heavy and cause larger headers/longer compile times; reports show anything from small (≈10%) to large slowdowns and examples where including in C++20 mode expanded include chains and slowed compilation. [2][3][4]
  • Runtime cost: views are designed to be zero‑overhead (lazy, inlinable pipelines) and in typical cases compile to equivalent iterator/loop code; benchmarks often show similar runtime performance to equivalent iterator-based code. [1][5]
  • Variability: compile-time and runtime behavior depend heavily on compiler (GCC/Clang vs MSVC), standard-library implementation, and how complex the composed views/closures are — some implementations (range-v3, older libstdc++) were worse for compile time than modern std::ranges implementations. [2][3]
  • Mitigations: use precompiled headers, hide ranges-heavy code behind translation-unit boundaries (compile-time walls), update compilers/stdlibs, avoid huge composed pipelines in headers, or use runtime type-erasure for APIs where compile cost dominates. Tools like -ftime-report (GCC) can show where compile time is spent. [6][3]

Key references

  • Practical benchmarks / discussion (build & runtime comparisons). [1]
  • Fluent C++ on limitations and compile-time cost of range-v3. [2]
  • Reports about larger headers and measurable compile-time increases when C++20 ranges are enabled. [3]
  • Design/behaviour discussion of std::ranges (constness/caching, performance implications). [4]
  • Benchmarks showing runtime parity with iterator/algorithm code. [5]
  • GCC compile-time analysis / tooling (-ftime-report). [6]

If you want, I can: (a) run or find specific microbenchmarks comparing compile time and runtime on GCC/Clang/MSVC for your code size, or (b) give concrete recipes (PCH, TU boundaries, example refactors) to reduce build-time impact.


@knst Great question! Let me address both aspects:

Library vs Language Feature

std::views::filter and std::views::transform are library features, specifically part of the C++20 Ranges library in the standard library. They are not core language features but rather template-based abstractions provided by <ranges>.

Compilation Time Impact

Your concern about compilation time is well-founded. Based on current research:

The Reality:

  • C++20 ranges/views commonly increase compile times (sometimes noticeably) but usually have no runtime cost if the compiler inlines and optimizes the pipeline
  • Reports show anything from small (≈10%) to large slowdowns depending on the complexity of the pipeline and compiler version
  • The template-heavy nature of ranges means more work for the compiler during instantiation

Mitigating Factors for This Specific Case:

  1. The usage in this PR is relatively simple (just filter + transform), not a deeply nested pipeline
  2. It's in a .cpp file, not a header, so the compilation impact is localized to net.cpp only
  3. Modern compilers (up-to-date GCC, Clang, MSVC) handle ranges better than older versions

Trade-offs:

  • Compilation time: Likely a modest increase in net.cpp compile time
  • Runtime performance: Views are designed to be zero-overhead (lazy, inlinable pipelines) and in typical cases compile to equivalent iterator/loop code
  • Code readability: More declarative and easier to understand the intent

Recommendation:
Given that this is localized to a single translation unit and uses a simple pipeline, the compilation time impact should be manageable. If it becomes a concern during actual builds, the team could consider measuring it or reverting to the traditional loop approach. The readability benefits likely outweigh the modest compilation time cost in this case.

@PastaPastaPasta PastaPastaPasta merged commit 730b018 into dashpay:develop Oct 21, 2025
33 of 38 checks passed
@PastaPastaPasta PastaPastaPasta added this to the 23.1 milestone Oct 21, 2025
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.

4 participants