-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor: separate network and consensus logic 2a/N [signing] #6992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: separate network and consensus logic 2a/N [signing] #6992
Conversation
|
WalkthroughThis PR adds a NetSigning NetHandler (header + implementation) and wires it into PeerManager; moves recovered-signature network handling and a worker thread into NetSigning. CSigningManager constructor no longer requires chainstate and removes worker-thread lifecycle methods while gaining VerifyAndProcessRecoveredSig, FetchPendingReconstructed, GetListeners, and modified CollectPendingRecoveredSigsToVerify. CSigSharesManager::TryRecoverSig now returns std::shared_ptr. PeerManagerInternal gains PeerPostProcessMessage. Build/test/lint entries updated to include the new files and adjusted dependencies. Sequence Diagram(s)sequenceDiagram
participant PM as PeerManager
participant NS as NetSigning
participant SM as CSigningManager
participant QM as CQuorumManager
Note over PM,NS: Incoming network recovered-sig (QSIGREC)
PM->>NS: ProcessMessage(QSIGREC, vRecv)
NS->>NS: Deserialize CRecoveredSig\nErase pending requests
NS->>SM: VerifyAndProcessRecoveredSig(from, recoveredSig)
SM->>QM: Lookup quorum / pubkey
alt quorum found & valid
SM->>SM: ProcessRecoveredSig(recoveredSig)
SM->>NS: Notify listeners / emit signals
NS->>PM: Post processed result via PeerPostProcessMessage
else missing/invalid quorum
SM-->>NS: return / ignore
end
Note over NS: Background worker thread
NS->>NS: WorkThreadMain()
loop periodic
NS->>SM: FetchPendingReconstructed()
NS->>SM: CollectPendingRecoveredSigsToVerify(...)
SM-->>NS: pubkeys + sig shares
NS->>NS: Batch verify sig shares, handle misbehavior
NS->>SM: ProcessRecoveredSig(validRecoveredSig)
end
sequenceDiagram
participant SS as SigSharesManager
participant SM as CSigningManager
participant NS as NetSigning
Note over SS,SM: Local signature recovery attempt
SS->>SS: TryRecoverSig(...)
alt recovery succeeded
SS-->>SM: returns shared_ptr<CRecoveredSig>
SM->>SM: ProcessRecoveredSig(shared_ptr)
SM->>NS: Notify listeners / emit signals
else recovery failed
SS-->>SS: returns nullptr
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/net_processing.cpp (2)
660-661: Document semantics ofPeerPostProcessMessagefor external callersThe new API is straightforward, but its contract isn’t obvious: calling it currently uses a sentinel node id (
-1in the implementation), which means:
- any
m_errorwill not actually punish a specific peer, and- any
m_to_erasewill not clear per‑peer object download state.It would help future users to add a brief comment here (and/or in the class header) stating that this entry point is intended only for generic post‑processing (relays, DSQs, etc.) where a concrete
NodeIdis not available, and that peer‑scoped effects are effectively ignored.
6556-6559: Avoid magic-1node id literal inPeerPostProcessMessageUsing
-1directly as the “no peer”NodeIdworks (callers likeMisbehaving/EraseObjectRequestsafely no‑op), but it’s a magic value and already appears elsewhere in this file for the same purpose.Consider introducing a named sentinel (e.g.
constexpr NodeId NO_PEER_ID{-1};) or reusing an existing one if present, and calling:- PostProcessMessage(std::move(ret), -1); + PostProcessMessage(std::move(ret), NO_PEER_ID);This keeps the intent clear and avoids repeating the raw literal.
src/llmq/net_signing.h (1)
15-45: MakeNetSigningheader self-contained (forward declareCRecoveredSig, include<memory>)The
NetSigninginterface looks aligned with the new design (a dedicated net handler aroundCSigningManager, with its own worker thread and interrupt), but the header currently depends on transitive declarations:
ProcessRecoveredSigusesstd::shared_ptr<const llmq::CRecoveredSig>without a visible declaration ofllmq::CRecoveredSig.<memory>is not included even thoughstd::shared_ptrappears in the public API.To avoid brittle include-order dependencies and keep the header self-contained, consider:
#include <net_processing.h> #include <util/threadinterrupt.h> #include <util/time.h> +#include <memory> #include <thread> -namespace llmq { -class CSigningManager; -} // namespace llmq +namespace llmq { +class CSigningManager; +class CRecoveredSig; +} // namespace llmqThis should not change behavior but will make future usage of
llmq/net_signing.hsafer regardless of inclusion order.src/llmq/signing_shares.cpp (2)
783-795: Code duplication acknowledged in TODO.The recovered signature processing logic (lines 785-793) duplicates code from
NetSigning::ProcessRecoveredSig(net_signing.cpp lines 62-67). The TODO comment on line 786 already acknowledges this.Consider extracting this common logic to a shared helper method in a follow-up to reduce duplication.
798-879: Verify single-member recovery logic flow.The single-member quorum recovery path has a two-stage check:
- Lines 817-832: Prepares
singleMemberRecoveredSigifis_single_member()- Lines 846-848: Returns
singleMemberRecoveredSigifis_single_member()While logically correct (both checks should match), this pattern is fragile because
singleMemberRecoveredSigis only initialized inside the firstifblock. If the checks ever diverge due to future changes, line 847 could return an uninitialized shared_ptr.Consider restructuring for clarity, such as an early return after line 831 for single-member quorums.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
src/Makefile.am(2 hunks)src/evo/mnhftx.cpp(0 hunks)src/init.cpp(2 hunks)src/llmq/context.cpp(1 hunks)src/llmq/net_signing.cpp(1 hunks)src/llmq/net_signing.h(1 hunks)src/llmq/signing.cpp(9 hunks)src/llmq/signing.h(7 hunks)src/llmq/signing_shares.cpp(4 hunks)src/llmq/signing_shares.h(2 hunks)src/net_processing.cpp(2 hunks)src/net_processing.h(1 hunks)src/test/evo_islock_tests.cpp(0 hunks)src/test/fuzz/process_message.cpp(0 hunks)test/lint/lint-circular-dependencies.py(2 hunks)
💤 Files with no reviewable changes (3)
- src/test/fuzz/process_message.cpp
- src/evo/mnhftx.cpp
- src/test/evo_islock_tests.cpp
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
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.
📚 Learning: 2024-12-29T17:43:41.755Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/quorums.cpp:224-224
Timestamp: 2024-12-29T17:43:41.755Z
Learning: The `CQuorumManager` is fully initialized by `LLMQContext`, addressing any concerns about the manager’s initialization sequence.
Applied to files:
src/llmq/context.cpp
📚 Learning: 2025-01-02T21:50:00.967Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/context.cpp:42-43
Timestamp: 2025-01-02T21:50:00.967Z
Learning: LLMQContext manages concurrency for the `CInstantSendManager`. Previously, this was handled globally; now it's handled as a class member in `LLMQContext`, but the concurrency control remains consistent.
Applied to files:
src/llmq/context.cpp
📚 Learning: 2025-10-02T18:29:54.756Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6840
File: src/net_processing.cpp:2882-2886
Timestamp: 2025-10-02T18:29:54.756Z
Learning: Across net_processing.cpp, once LLMQContext (m_llmq_ctx) is asserted non-null, its subcomponents (e.g., isman, qdkgsman, quorum_block_processor) are treated as initialized and used without extra null checks.
Applied to files:
src/llmq/context.cppsrc/init.cppsrc/llmq/net_signing.hsrc/llmq/net_signing.cppsrc/net_processing.cppsrc/Makefile.amsrc/llmq/signing.cppsrc/llmq/signing.h
📚 Learning: 2025-07-15T14:53:04.819Z
Learnt from: knst
Repo: dashpay/dash PR: 6691
File: src/test/llmq_params_tests.cpp:148-151
Timestamp: 2025-07-15T14:53:04.819Z
Learning: In the Dash Core LLMQ implementation, signingActiveQuorumCount is never 0 in the actual parameters defined in params.h, making division by zero scenarios unrealistic in the max_cycles() function.
Applied to files:
src/llmq/context.cppsrc/llmq/signing.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/llmq/signing.cpp
🧬 Code graph analysis (7)
src/net_processing.h (1)
src/net_processing.cpp (4)
PeerPostProcessMessage(6556-6559)PeerPostProcessMessage(6556-6556)ret(660-660)ret(683-683)
src/llmq/signing_shares.h (2)
test/functional/test_framework/messages.py (1)
CRecoveredSig(1520-1544)src/llmq/signing_shares.cpp (2)
TryRecoverSig(798-879)TryRecoverSig(798-799)
src/llmq/net_signing.h (4)
src/llmq/signing.cpp (4)
CSigningManager(327-331)CSigningManager(333-333)ProcessRecoveredSig(475-514)ProcessRecoveredSig(475-475)src/llmq/signing.h (2)
CSigningManager(158-233)nodiscard(69-91)src/net_processing.h (3)
NetHandler(70-85)NetHandler(74-77)PeerManagerInternal(58-68)src/llmq/net_signing.cpp (12)
ProcessMessage(19-34)ProcessMessage(19-19)ProcessPendingRecoveredSigs(70-131)ProcessPendingRecoveredSigs(70-70)ProcessRecoveredSig(58-68)ProcessRecoveredSig(58-58)Start(36-44)Start(36-36)Stop(46-56)Stop(46-46)WorkThreadMain(133-148)WorkThreadMain(133-133)
src/llmq/net_signing.cpp (3)
src/net_processing.cpp (15)
ProcessMessage(3667-5457)ProcessMessage(3667-3672)pfrom(618-618)pfrom(639-640)pfrom(751-753)pfrom(762-762)pfrom(770-770)pfrom(773-773)pfrom(775-775)pfrom(777-777)pfrom(873-873)pfrom(1055-1055)WITH_LOCK(331-334)WITH_LOCK(3232-3232)WITH_LOCK(3254-3254)src/llmq/net_signing.h (1)
Start(33-35)src/llmq/signing.cpp (2)
ProcessRecoveredSig(475-514)ProcessRecoveredSig(475-475)
src/llmq/signing_shares.cpp (2)
src/llmq/signhash.cpp (1)
SignHash(14-22)src/llmq/signhash.h (1)
SignHash(24-46)
src/llmq/signing.cpp (2)
src/llmq/signing.h (1)
CSigningManager(158-233)src/llmq/net_signing.cpp (2)
ProcessRecoveredSig(58-68)ProcessRecoveredSig(58-58)
src/llmq/signing.h (3)
src/llmq/signhash.cpp (1)
SignHash(14-22)src/llmq/signhash.h (2)
SignHash(24-46)hash(64-73)src/llmq/signing.cpp (18)
CSigningManager(327-331)CSigningManager(333-333)AlreadyHave(335-348)AlreadyHave(335-335)GetRecoveredSigForGetData(350-360)GetRecoveredSigForGetData(350-350)VerifyAndProcessRecoveredSig(362-394)VerifyAndProcessRecoveredSig(362-362)FetchPendingReconstructed(467-472)FetchPendingReconstructed(467-467)CollectPendingRecoveredSigsToVerify(396-465)CollectPendingRecoveredSigsToVerify(396-398)GetListeners(516-520)GetListeners(516-516)ProcessRecoveredSig(475-514)ProcessRecoveredSig(475-475)Cleanup(533-539)Cleanup(533-533)
⏰ 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: mac-build / Build source
- GitHub Check: linux64_ubsan-build / Build source
- GitHub Check: linux64_sqlite-build / Build source
- GitHub Check: linux64_fuzz-build / Build source
- GitHub Check: linux64-build / Build source
- GitHub Check: linux64_tsan-build / Build source
- GitHub Check: win64-build / Build source
- GitHub Check: arm-linux-build / Build source
- GitHub Check: linux64_nowallet-build / Build source
- GitHub Check: Lint / Run linters
🔇 Additional comments (21)
src/net_processing.h (1)
67-67: LGTM! Clean interface extension for post-processing.The addition of
PeerPostProcessMessageto thePeerManagerInternalinterface is well-structured. The use of an rvalue reference forMessageProcessingResultenables efficient move semantics for post-processing results. This aligns with the broader refactoring to decouple network and consensus logic.src/init.cpp (2)
94-94: LGTM! Header change reflects architectural separation.The switch from
llmq/signing.htollmq/net_signing.haligns with the PR objective to separate network-handling code from core signing logic. This supports the broader refactoring to decouple consensus/chain code from network/node code.
2203-2203: LGTM! NetSigning handler follows established patterns.The addition of the
NetSigningextra handler is consistent with the existingNetInstantSendhandler pattern (line 2202). Both dereferencenode.llmq_ctxsubcomponents (sigmanandismanrespectively) without explicit null checks, which is the established pattern for LLMQContext usage in this codebase. Based on learnings, this is acceptable as LLMQContext subcomponents are treated as initialized once the context is asserted non-null.src/Makefile.am (1)
274-275: NetSigning build integration looks consistentAdding
llmq/net_signing.htoBITCOIN_CORE_Handllmq/net_signing.cpptolibbitcoin_node_a_SOURCESmatches how other LLMQ components are wired intolibbitcoin_nodeand should build cleanly across targets.Also applies to: 539-539
src/llmq/context.cpp (1)
18-40: CSigningManager wiring matches new API; consider markingpeermanunusedConstructing
sigmanfrom*qmananddb_paramsaligns with the refactoredCSigningManagerinterface and cleanly removes chainstate/peer dependencies fromLLMQContext. With signing’s worker-thread handling moved out, keepingInterrupt()/Start()/Stop()focused onqmanandclhandlerlooks appropriate.The
PeerManager& peermanargument inStartis currently unused, which can trigger-Wunused-parameterunder stricter builds; consider(void)peerman;or[[maybe_unused]]to keep this future-proof.src/llmq/signing_shares.h (1)
10-11: SignHash include andTryRecoverSigreturn type are consistent with new flowPulling in
llmq/signhash.his appropriate given the directSignHashmembers inCSigSharesNodeState, and havingTryRecoverSigreturnstd::shared_ptr<CRecoveredSig>(possiblynullptr) cleanly exposes whether recovery succeeded to callers without altering locking annotations.Also applies to: 480-482
test/lint/lint-circular-dependencies.py (1)
24-64: Circular-dependency expectations updated in line with new LLMQ wiringThe updated
EXPECTED_CIRCULAR_DEPENDENCIESentries that now traversellmq/signing_shares -> net_processing -> …instead ofllmq/signingmatch the refactored dependency graph and keep the lint test aligned with the new architecture.src/llmq/net_signing.cpp (5)
36-44: LGTM!The thread lifecycle checks are appropriate. The assert on line 40 ensures Start() is not called multiple times, which is a programming error rather than a runtime condition.
46-56: LGTM!The shutdown sequence check is appropriate, ensuring
Interrupt()is called beforeStop().
58-68: LGTM!The recovered signature processing flow is correct. Early return when
ProcessRecoveredSigreturns false (indicating the signature was already processed) prevents redundant listener notifications.
70-131: LGTM!The batched verification flow is well-structured:
- Fetches pending reconstructed signatures and processes them immediately
- Collects pending signatures to verify (up to 32 unique sessions)
- Performs batched verification using quorum public keys
- Bans peers with invalid signatures
- Processes valid signatures
The use of
.at()on line 100 is safe becauseCollectPendingRecoveredSigsToVerifyensures only signatures with valid quorums are included inrecSigsByNode.
133-148: LGTM!The worker thread main loop follows the expected pattern: process pending work, perform periodic cleanup, and sleep when idle. The interrupt handling on line 144 ensures the thread can be cleanly shut down.
src/llmq/signing.cpp (6)
327-330: LGTM!The constructor signature change successfully removes the CChainState dependency, aligning with the PR objective to separate consensus/chain code from network/node code.
362-394: LGTM!The new entry point properly validates recovered signatures before queueing them for verification. The early returns for missing/inactive quorums and duplicate signatures are appropriate, and the cs_pending lock protects the pending map correctly.
396-465: LGTM!The refactored method now populates a map of BLS public keys (
ret_pubkeys) instead of full quorum objects. This is a cleaner separation of concerns, as the verification logic only needs the public keys, not the entire quorum state.The structured binding on line 436 and the key-based lookup pattern are appropriate for the new data flow.
467-472: LGTM!The method efficiently fetches and clears pending reconstructed signatures using atomic swap under lock. Clean implementation.
475-514: LGTM!The boolean return type appropriately indicates whether the recovered signature was newly processed (true) or already existed (false). This enables callers to skip redundant listener notifications, as seen in
NetSigning::ProcessRecoveredSig.
516-520: LGTM!The method returns a copy of the listeners vector under lock, allowing callers to iterate without holding cs_listeners. This prevents potential deadlocks.
src/llmq/signing.h (3)
37-37: LGTM!The forward declaration of
SignHashis appropriate for use in thebuildSignHash()return type, avoiding unnecessary header inclusion.
55-55: LGTM!Making the return type explicit improves API clarity and prevents potential issues with auto deduction.
179-209: LGTM!The API changes are well-documented and consistent with the implementations:
- Constructor removes CChainState dependency
- New
VerifyAndProcessRecoveredSigentry point for network-based processing- New methods (
FetchPendingReconstructed,CollectPendingRecoveredSigsToVerify,GetListeners) clearly documented as used by NetSigningThe lock annotations are appropriate for all methods.
1fd5c47 to
069797f
Compare
|
LGTM overall, a few small suggestions and should also maybe rebase to make sure all new lints are happy |
069797f to
933602e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/llmq/signing.cpp (1)
437-463: Potential issue: Erased sigs may leave dangling entries inretSigShares.When a quorum is not found or inactive (lines 445-456), the signature is erased from
vbut the outer loop continues. If all sigs for a node are erased, the node entry inretSigSharesremains with an empty list. The caller (ProcessPendingRecoveredSigsinnet_signing.cpp) iterates overretSigSharesand callspubkeys.at()for each sig, which could throw if the corresponding pubkey was never added.This is related to the concern raised in
net_signing.cpp. Consider either:
- Cleaning up empty entries in
retSigShares, or- Using
pubkeys.find()in the caller as suggested there.for (auto& [nodeId, v] : retSigShares) { for (auto it = v.begin(); it != v.end();) { // ... existing logic ... } + // Clean up empty entries + if (v.empty()) { + // Note: Can't erase during range-for; consider restructuring + } }
♻️ Duplicate comments (2)
src/llmq/net_signing.cpp (2)
29-32: LGTM - Previous issue addressed.The early return after
PeerMisbehavingis now correctly implemented, preventing invalid recovered signatures from being processed.
139-139: LGTM - Reviewer suggestion addressed.The
CLEANUP_INTERVALis now defined asconstexpras suggested in the past review.
🧹 Nitpick comments (2)
src/llmq/net_signing.cpp (1)
37-57: Consider usingAssume()instead of bareassert(false)for clearer intent.The
assert(false)statements at lines 41 and 51 could be replaced withAssume(condition)macros which provide better diagnostics and follow the codebase pattern for detecting programming errors. However, this is a minor stylistic preference and the current implementation is functionally correct.void NetSigning::Start() { // can't start new thread if we have one running already - if (workThread.joinable()) { - assert(false); - } + Assume(!workThread.joinable()); workThread = std::thread(&util::TraceThread, "recsigs", [this] { WorkThreadMain(); }); } void NetSigning::Stop() { // make sure to call InterruptWorkerThread() first - if (!workInterrupt) { - assert(false); - } + Assume(workInterrupt); if (workThread.joinable()) { workThread.join(); } }src/llmq/signing.h (1)
185-187: Consider makingVerifyAndProcessRecoveredSigconst-correct on the shared_ptrThe rest of the class stores recovered sigs as
std::shared_ptr<const CRecoveredSig>, while this new API takesstd::shared_ptr<CRecoveredSig>. Since the implementation only reads from the object and then enqueues it, you could tighten the signature to be const as well:- void VerifyAndProcessRecoveredSig(NodeId from, std::shared_ptr<CRecoveredSig> recovered_sig) + void VerifyAndProcessRecoveredSig(NodeId from, std::shared_ptr<const CRecoveredSig> recovered_sig) EXCLUSIVE_LOCKS_REQUIRED(!cs_pending);Callers with
std::shared_ptr<CRecoveredSig>will still compile thanks to the standardshared_ptrconverting constructor, but the interface will better reflect immutability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 069797f25cac30b3bf08be03e48fdd74b01bf38e and fc1ffdec81261cb28bda2752b4a7cc99535216ba.
📒 Files selected for processing (15)
src/Makefile.am(2 hunks)src/evo/mnhftx.cpp(0 hunks)src/init.cpp(2 hunks)src/llmq/context.cpp(1 hunks)src/llmq/net_signing.cpp(1 hunks)src/llmq/net_signing.h(1 hunks)src/llmq/signing.cpp(9 hunks)src/llmq/signing.h(7 hunks)src/llmq/signing_shares.cpp(4 hunks)src/llmq/signing_shares.h(2 hunks)src/net_processing.cpp(2 hunks)src/net_processing.h(1 hunks)src/test/evo_islock_tests.cpp(0 hunks)src/test/fuzz/process_message.cpp(0 hunks)test/lint/lint-circular-dependencies.py(2 hunks)
💤 Files with no reviewable changes (3)
- src/evo/mnhftx.cpp
- src/test/evo_islock_tests.cpp
- src/test/fuzz/process_message.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
- src/init.cpp
- src/llmq/signing_shares.cpp
🧰 Additional context used
📓 Path-based instructions (5)
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.hsrc/llmq/net_signing.cppsrc/llmq/net_signing.hsrc/llmq/signing_shares.hsrc/net_processing.cppsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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/llmq/net_signing.cppsrc/llmq/net_signing.hsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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.cppsrc/llmq/net_signing.hsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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.cppsrc/llmq/net_signing.hsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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/llmq/net_signing.cppsrc/llmq/net_signing.hsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.h
🧠 Learnings (16)
📓 Common learnings
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.
📚 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/Makefile.amsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/signing.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/Makefile.amsrc/llmq/net_signing.cppsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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.amsrc/llmq/signing.cppsrc/llmq/signing.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} : LLMQ quorums must support multiple configurations (50/60, 400/60, 400/85) for different services (ChainLocks, InstantSend, governance voting)
Applied to files:
src/Makefile.amsrc/llmq/net_signing.cppsrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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} : ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention
Applied to files:
src/Makefile.amtest/lint/lint-circular-dependencies.pysrc/llmq/signing_shares.hsrc/llmq/signing.cppsrc/llmq/signing.h
📚 Learning: 2025-10-02T18:29:54.756Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6840
File: src/net_processing.cpp:2882-2886
Timestamp: 2025-10-02T18:29:54.756Z
Learning: Across net_processing.cpp, once LLMQContext (m_llmq_ctx) is asserted non-null, its subcomponents (e.g., isman, qdkgsman, quorum_block_processor) are treated as initialized and used without extra null checks.
Applied to files:
src/Makefile.amsrc/llmq/net_signing.cppsrc/net_processing.cppsrc/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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:
test/lint/lint-circular-dependencies.pysrc/llmq/net_signing.cppsrc/llmq/signing.cppsrc/llmq/signing.h
📚 Learning: 2025-07-15T14:53:04.819Z
Learnt from: knst
Repo: dashpay/dash PR: 6691
File: src/test/llmq_params_tests.cpp:148-151
Timestamp: 2025-07-15T14:53:04.819Z
Learning: In the Dash Core LLMQ implementation, signingActiveQuorumCount is never 0 in the actual parameters defined in params.h, making division by zero scenarios unrealistic in the max_cycles() function.
Applied to files:
src/llmq/signing.cppsrc/llmq/context.cppsrc/llmq/signing.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/llmq/signing.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,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/llmq/signing.cpp
📚 Learning: 2024-12-29T17:43:41.755Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/quorums.cpp:224-224
Timestamp: 2024-12-29T17:43:41.755Z
Learning: The `CQuorumManager` is fully initialized by `LLMQContext`, addressing any concerns about the manager’s initialization sequence.
Applied to files:
src/llmq/context.cpp
📚 Learning: 2025-01-02T21:50:00.967Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/context.cpp:42-43
Timestamp: 2025-01-02T21:50:00.967Z
Learning: LLMQContext manages concurrency for the `CInstantSendManager`. Previously, this was handled globally; now it's handled as a class member in `LLMQContext`, but the concurrency control remains consistent.
Applied to files:
src/llmq/context.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/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files
Applied to files:
src/llmq/signing.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/llmq/signing.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,evo,coinjoin,governance}/**/*.{cpp,h} : Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures
Applied to files:
src/llmq/signing.h
🧬 Code graph analysis (6)
src/net_processing.h (1)
src/net_processing.cpp (4)
PeerPostProcessMessage(6561-6564)PeerPostProcessMessage(6561-6561)ret(655-655)ret(678-678)
src/llmq/net_signing.cpp (2)
src/llmq/net_signing.h (1)
Start(33-35)src/llmq/signing.cpp (2)
ProcessRecoveredSig(476-515)ProcessRecoveredSig(476-476)
src/llmq/net_signing.h (2)
src/llmq/signing.h (2)
CSigningManager(158-233)nodiscard(69-91)src/net_processing.h (3)
NetHandler(70-85)NetHandler(74-77)PeerManagerInternal(58-68)
src/llmq/signing_shares.h (2)
test/functional/test_framework/messages.py (1)
CRecoveredSig(1520-1544)src/llmq/signing_shares.cpp (2)
TryRecoverSig(807-888)TryRecoverSig(807-808)
src/llmq/signing.cpp (2)
src/llmq/signing.h (1)
CSigningManager(158-233)src/llmq/net_signing.cpp (2)
ProcessRecoveredSig(59-69)ProcessRecoveredSig(59-59)
src/llmq/signing.h (3)
src/llmq/signhash.h (2)
SignHash(24-46)hash(64-73)src/llmq/signing.cpp (10)
CSigningManager(328-332)CSigningManager(334-334)AlreadyHave(336-349)AlreadyHave(336-336)VerifyAndProcessRecoveredSig(363-395)VerifyAndProcessRecoveredSig(363-363)GetListeners(517-521)GetListeners(517-517)ProcessRecoveredSig(476-515)ProcessRecoveredSig(476-476)src/llmq/net_signing.cpp (2)
ProcessRecoveredSig(59-69)ProcessRecoveredSig(59-59)
🔇 Additional comments (25)
test/lint/lint-circular-dependencies.py (1)
26-63: Verify that the circular dependency lint passes and check for obsolete entries.The updated dependency paths consistently introduce
llmq/signing_sharesas an intermediate component between signing logic and net_processing, which aligns with the PR's goal of separating network and consensus code. Ensure that:
- The lint script passes with these changes
- Any old dependency paths that are now replaced by the new
llmq/signing_sharespaths are removed from the expected list (e.g., paths previously routing throughllmq/signingdirectly)src/net_processing.h (1)
67-67: LGTM! Clean interface extension for post-processing.The new pure virtual method provides a clean callback mechanism for external handlers (like NetSigning) to post-process message results. The signature correctly uses move semantics with
MessageProcessingResult&&, and the implementation appropriately forwards to the existingPostProcessMessageinfrastructure.src/Makefile.am (1)
276-276: LGTM! Standard build integration for new NetSigning component.The header and source files are correctly added to their respective lists (
BITCOIN_CORE_Handlibbitcoin_node_a_SOURCES), properly placed within the llmq section, and formatted consistently with existing entries.Also applies to: 539-539
src/net_processing.cpp (1)
655-656: Clarify semantics ofNodeId = -1inPeerPostProcessMessageForwarding through
PostProcessMessage(std::move(ret), /*NodeId=*/-1)preserves relay behavior (txs, invs, DSQ) but effectively dropsm_errorandm_to_erasehandling, sinceMisbehaving()/EraseObjectRequest()no-op for non-existent peers (e.g.NodeId = -1). If all currentPeerPostProcessMessagecall sites are truly peer-agnostic and either never set these fields or handle misbehavior directly, this is fine and consistent with existingNodeId = -1usage (e.g. orphan processing). Otherwise, you may eventually want a variant that also threads the originatingNodeId.src/llmq/signing_shares.h (2)
10-10: LGTM!The
signhash.hinclude is appropriately added sinceSignHashis used in theSessionInfoandSessionstructs within this file.
482-483: LGTM!The return type change from
voidtostd::shared_ptr<CRecoveredSig>aligns with the implementation insigning_shares.cppand enables callers to receive and propagate the recovered signature to listeners and main signals.src/llmq/context.cpp (2)
33-33: LGTM!The simplified
CSigningManagerconstruction removing theChainstateManagerdependency aligns with the refactor that decouples consensus/chain code from network/node code.
46-58: LGTM!The removal of worker thread lifecycle calls (
InterruptWorkerThread,StartWorkerThread,StopWorkerThread) fromLLMQContextis correct since this responsibility has been moved to the newNetSigninghandler which manages its own worker thread lifecycle.src/llmq/net_signing.h (1)
19-45: LGTM!The
NetSigningclass is well-designed:
- Properly extends
NetHandlerwith appropriate lifecycle methods (Start,Stop,Interrupt)- Constructor correctly initializes the thread interrupt and stores the signing manager reference
- Worker thread pattern follows existing conventions in the codebase
src/llmq/net_signing.cpp (3)
59-69: LGTM!The
ProcessRecoveredSigflow correctly:
- Delegates to the signing manager for persistence
- Notifies all registered listeners
- Emits main signals for broader observers
71-132: LGTM - Well-structured batch verification.The
ProcessPendingRecoveredSigsimplementation correctly:
- Fetches pending reconstructed sigs first
- Uses insecure batch verification (appropriate since verifying against quorum public keys)
- Properly handles bad sources by banning misbehaving peers
- Uses a
processedset to avoid duplicate processing
100-102: Verifypubkeys.at()safety - defensive check recommended for missing quorum keysThe
pubkeys.at()call at line 101 will throwstd::out_of_rangeif the key doesn't exist. WhileCollectPendingRecoveredSigsToVerifypopulatespubkeysfor valid quorums, a defensive check usingfind()should be added to handle edge cases where a recovered signature references a quorum that may not have a corresponding public key in the map:- const auto& pubkey = pubkeys.at(std::make_pair(recSig->getLlmqType(), recSig->getQuorumHash())); + auto it = pubkeys.find(std::make_pair(recSig->getLlmqType(), recSig->getQuorumHash())); + if (it == pubkeys.end()) { + batchVerifier.badSources.emplace(nodeId); + break; + } + const auto& pubkey = it->second;src/llmq/signing.cpp (5)
328-332: LGTM!The simplified constructor removing
ChainstateManagerdependency aligns with the refactor goal of decoupling consensus/chain code from network/node code.
363-395: LGTM!The
VerifyAndProcessRecoveredSigmethod correctly:
- Centralizes quorum lookup and active-quorum validation
- Returns early for already-seen signatures
- Stores pending sigs per-node for later batch verification
468-473: LGTM!The
FetchPendingReconstructedmethod atomically swaps out the pending reconstructed sigs under lock, which is a clean pattern for transferring ownership.
476-515: LGTM!The
ProcessRecoveredSigmethod now returns a boolean indicating whether the sig was newly processed, enabling callers to determine if listeners should be notified. The duplicate/conflict detection logic is preserved correctly.
517-521: LGTM!The
GetListenersaccessor properly acquires the lock before returning a copy of the listeners vector, ensuring thread safety.src/llmq/signing.h (8)
16-16: unordered_lru_cache include placement is appropriateIncluding
<unordered_lru_cache.h>here matches the usage inCRecoveredSigsDband keeps the LLMQ-specific caching self-contained in this header, in line with the project guidelines for llmq DB/caching code.
37-37: Forward declaration ofSignHashcleanly breaks the header dependencyForward-declaring
llmq::SignHashhere is a good way to avoid pulling insignhash.hand helps with circular-dependency reduction, as long as the definition ofCSigBase::buildSignHash()lives in a.cppthat includessignhash.h(which it appears to do from the provided snippet).
55-55: Explicit return type forgetLlmqType()improves claritySwitching from
autotoConsensus::LLMQTypeforgetLlmqType()makes the API clearer and avoids potential template/ABI surprises without changing behavior.
154-155: TODO on simplifyingHandleNewRecoveredSigreturn type is fine to leave for laterThe new TODO pointing to a
std::variant-based result is a reasonable future refactor note; keeping it out of this structural PR avoids scope creep in consensus-adjacent code.
179-179: Constructor decoupling fromCChainStatealigns with PR goalAdjusting
CSigningManager’s ctor to depend only onCQuorumManagerandDbWrapperParamsremoves the chainstate dependency and cleanly pushes node/network concerns out of this component, which is exactly what this PR is aiming for.
199-209: NetSigning-facing helpers look well-factored and respect locking patternsThe new trio of methods
FetchPendingReconstructed(),CollectPendingRecoveredSigsToVerify(...)(now exposingCBLSPublicKeyinstead ofCQuorumCPtr), andGetListeners(),plus the clarified comment and bool-returning
ProcessRecoveredSig(...), provide a clean separation between the network handler and the signing manager while keeping all internal state guarded bycs_pending/cs_listeners. Returning copies by value avoids holding locks during downstream processing, which is a good trade-off here.
211-211: Private section for CSigSharesManager helpers keeps surface minimalReintroducing a
private:section beforeGetDb()and theCSigSharesManagerfriendship keeps these helpers out of the public API while still exposing what the share manager needs, which is a sensible encapsulation boundary.
232-232: PublicCleanup()hook remains small and focusedKeeping
void Cleanup();as a standalone public hook fits well with the refactor that removed internal worker-thread management; it gives external orchestrators a clear, single entry point for db/cache cleanup without re-introducing node/peer dependencies.
fc1ffde to
dc00dce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/llmq/net_signing.cpp (2)
37-57: Consider replacingassert(false)withAssume(false)or throwing an exception.Using
assert(false)for error conditions (lines 41 and 51) may be silently ignored in release builds compiled withNDEBUG. If these are invariants that must hold, consider usingAssume(false)which logs in release builds, or throw an exception to ensure the error is always caught.void NetSigning::Start() { // can't start new thread if we have one running already if (workThread.joinable()) { - assert(false); + Assume(false && "NetSigning::Start called with running thread"); + return; } workThread = std::thread(&util::TraceThread, "recsigs", [this] { WorkThreadMain(); }); } void NetSigning::Stop() { // make sure to call InterruptWorkerThread() first if (!workInterrupt) { - assert(false); + Assume(false && "NetSigning::Stop called without Interrupt"); } if (workThread.joinable()) { workThread.join(); } }
114-120: Consider using C++20contains()instead ofcount().Since Dash Core requires C++20 (per coding guidelines), you can use the more expressive
contains()method:- if (batchVerifier.badSources.count(nodeId)) { + if (batchVerifier.badSources.contains(nodeId)) {src/llmq/signing.cpp (1)
437-463: Consider using C++20contains()for consistency.Similar to the suggestion in
net_signing.cpp, line 443 could usecontains():- if (!ret_pubkeys.count(quorumKey)) { + if (!ret_pubkeys.contains(quorumKey)) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between fc1ffdec81261cb28bda2752b4a7cc99535216ba and dc00dce6b3b6166b25f0f0d2479227db536ae753.
📒 Files selected for processing (3)
src/llmq/net_signing.cpp(1 hunks)src/llmq/signing.cpp(9 hunks)src/net_processing.cpp(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/net_processing.cpp
🧰 Additional context used
📓 Path-based instructions (5)
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/llmq/net_signing.cppsrc/llmq/signing.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.cppsrc/llmq/signing.cpp
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.cppsrc/llmq/signing.cpp
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.cppsrc/llmq/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.cppsrc/llmq/signing.cpp
🧠 Learnings (11)
📓 Common learnings
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.
📚 Learning: 2025-10-02T18:29:54.756Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6840
File: src/net_processing.cpp:2882-2886
Timestamp: 2025-10-02T18:29:54.756Z
Learning: Across net_processing.cpp, once LLMQContext (m_llmq_ctx) is asserted non-null, its subcomponents (e.g., isman, qdkgsman, quorum_block_processor) are treated as initialized and used without extra null checks.
Applied to files:
src/llmq/net_signing.cppsrc/llmq/signing.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/llmq/net_signing.cppsrc/llmq/signing.cpp
📚 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/llmq/net_signing.cpp
📚 Learning: 2025-07-15T14:53:04.819Z
Learnt from: knst
Repo: dashpay/dash PR: 6691
File: src/test/llmq_params_tests.cpp:148-151
Timestamp: 2025-07-15T14:53:04.819Z
Learning: In the Dash Core LLMQ implementation, signingActiveQuorumCount is never 0 in the actual parameters defined in params.h, making division by zero scenarios unrealistic in the max_cycles() function.
Applied to files:
src/llmq/signing.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} : LLMQ quorums must support multiple configurations (50/60, 400/60, 400/85) for different services (ChainLocks, InstantSend, governance voting)
Applied to files:
src/llmq/signing.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/llmq/signing.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} : ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention
Applied to files:
src/llmq/signing.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/llmq/signing.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/llmq/signing.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,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/llmq/signing.cpp
🧬 Code graph analysis (2)
src/llmq/net_signing.cpp (1)
src/llmq/net_signing.h (1)
Start(33-35)
src/llmq/signing.cpp (2)
src/llmq/signing.h (1)
CSigningManager(158-233)src/llmq/net_signing.cpp (2)
ProcessRecoveredSig(59-69)ProcessRecoveredSig(59-59)
🔇 Additional comments (9)
src/llmq/net_signing.cpp (4)
19-35: LGTM! ProcessMessage handles QSIGREC correctly.The message processing flow is well-structured: deserialize, clear pending request, validate LLMQ type with early return on failure, then delegate to signature manager. The early return after
PeerMisbehaving(line 31) correctly prevents processing invalid signatures.
59-69: LGTM! ProcessRecoveredSig correctly notifies listeners and main signals.The function properly chains processing through the signature manager, iterates over listener copies (thread-safe), and emits main signals for observers.
134-149: LGTM! Worker thread main loop follows standard patterns.The cleanup interval is appropriately declared as
constexpr. The loop correctly handles interruption, periodic cleanup, and idle sleeping. The TODO comment on line 144 is noted for future improvement.
100-104: Potentialstd::out_of_rangeexception if pubkey lookup fails at line 101.
pubkeys.at()will throw if the key is not found. WhileCollectPendingRecoveredSigsToVerifyshould populate the map and synchronously remove both signatures and pubkeys when a quorum is unavailable, consider adding a defensive check usingfind()instead ofat()or verifying the key exists before access for added robustness.src/llmq/signing.cpp (5)
328-332: LGTM! Constructor simplification aligns with separation of concerns.The constructor now only takes
CQuorumManagerand database parameters, removing the previous chainstate dependency as intended by this refactoring PR.
363-395: LGTM! VerifyAndProcessRecoveredSig provides proper validation pipeline.The function correctly validates quorum existence and activity, checks for duplicates, and queues signatures for batch verification. Proper locking is used for the pending signatures map.
468-473: LGTM! FetchPendingReconstructed uses efficient swap pattern.The atomic swap under lock ensures thread safety while efficiently transferring ownership of pending reconstructed signatures.
476-515: LGTM! ProcessRecoveredSig correctly returns status for caller coordination.The function properly handles:
- Duplicate detection (line 480-482)
- Conflict detection with appropriate logging (lines 489-509)
- Atomic DB write followed by pending cleanup (lines 511-512)
- Returns
trueonly when the signature was newly written, enablingNetSigningto coordinate listener notifications.
517-521: LGTM! GetListeners returns a thread-safe copy.Returning a copy of the listeners vector allows callers to iterate without holding the lock, preventing potential deadlocks and ensuring thread safety.
UdjinM6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK dc00dce6b3b6166b25f0f0d2479227db536ae753
|
This pull request has conflicts, please rebase. |
… inside TryRecoverSig
…der llmq/signing.h
…rocessRecoveredSig ex-ProcessRecoveredSig
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
dc00dce to
094b5a4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/llmq/signing_shares.h (1)
10-11: Header updates align with the new SignHash and recovery APIImporting
<llmq/signhash.h>and changingTryRecoverSigto returnstd::shared_ptr<CRecoveredSig>(with unchangedEXCLUSIVE_LOCKS_REQUIRED(!cs)annotation) matches the implementation and lets callers drive post‑recovery handling without altering locking semantics.If you want to emphasise that recovered sigs aren’t mutated after construction, a future follow‑up could switch the return type to
std::shared_ptr<const CRecoveredSig>.Also applies to: 481-484
src/llmq/signing_shares.cpp (1)
743-807: Recovered-signature flow fromProcessSigShare/TryRecoverSiglooks correct; shared post-processing could be factored outAfter adding a verified share,
ProcessSigShareonly attempts recovery once the per-session count reaches the quorum threshold, then callsTryRecoverSigoutsidecs, which in turn:
- Re-checks
HasRecoveredSigForIdto avoid duplicate work,- Builds the relevant sign hash and collects only the shares for that session under
cs,- Handles single‑member quorums by directly wrapping the member’s share in a
CRecoveredSig, and- For multi‑member quorums, performs BLS
Recoverand occasional self‑verification outside the lock.On success, the caller’s
sigman.ProcessRecoveredSig(rs)followed by iteratingsigman.GetListeners()and invokingm_peerman.PostProcessMessage(...)plusGetMainSignals().NotifyRecoveredSig(rs, …, proactive_relay)matches the NetSigning path and keeps lock hold times short. The dedicatedNotifyRecoveredSighelper that wrapsRelayRecoveredSigalso fits this design.To avoid divergence between CSigSharesManager and NetSigning over time, you might later extract the common “post‑ProcessRecoveredSig” block (listeners iteration, proactive_relay decision, and
NotifyRecoveredSig/GetMainSignals().NotifyRecoveredSigcall) into a shared helper inCSigningManageror a small utility.Also applies to: 810-891, 988-991
src/llmq/net_signing.cpp (1)
143-143: Consider extracting the cleanup interval constant.As suggested in a previous review, the cleanup interval could be extracted for better readability and maintainability.
Apply this diff:
+ constexpr auto CLEANUP_INTERVAL{5s}; - constexpr auto CLEANUP_INTERVAL{5s}; - if (cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) { + if (cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between dc00dce6b3b6166b25f0f0d2479227db536ae753 and 094b5a4.
📒 Files selected for processing (15)
src/Makefile.am(2 hunks)src/evo/mnhftx.cpp(0 hunks)src/init.cpp(2 hunks)src/llmq/context.cpp(1 hunks)src/llmq/net_signing.cpp(1 hunks)src/llmq/net_signing.h(1 hunks)src/llmq/signing.cpp(9 hunks)src/llmq/signing.h(7 hunks)src/llmq/signing_shares.cpp(4 hunks)src/llmq/signing_shares.h(2 hunks)src/net_processing.cpp(2 hunks)src/net_processing.h(1 hunks)src/test/evo_islock_tests.cpp(0 hunks)src/test/fuzz/process_message.cpp(0 hunks)test/lint/lint-circular-dependencies.py(2 hunks)
💤 Files with no reviewable changes (3)
- src/test/evo_islock_tests.cpp
- src/test/fuzz/process_message.cpp
- src/evo/mnhftx.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
- src/net_processing.cpp
- src/net_processing.h
- src/llmq/net_signing.h
🧰 Additional context used
📓 Path-based instructions (5)
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/init.cppsrc/llmq/signing_shares.hsrc/llmq/net_signing.cppsrc/llmq/context.cppsrc/llmq/signing_shares.cppsrc/llmq/signing.cppsrc/llmq/signing.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/llmq/signing_shares.hsrc/llmq/net_signing.cppsrc/llmq/context.cppsrc/llmq/signing_shares.cppsrc/llmq/signing.cppsrc/llmq/signing.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/signing_shares.hsrc/llmq/net_signing.cppsrc/llmq/context.cppsrc/llmq/signing_shares.cppsrc/llmq/signing.cppsrc/llmq/signing.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/signing_shares.hsrc/llmq/net_signing.cppsrc/llmq/context.cppsrc/llmq/signing_shares.cppsrc/llmq/signing.cppsrc/llmq/signing.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/llmq/signing_shares.hsrc/llmq/net_signing.cppsrc/llmq/context.cppsrc/llmq/signing_shares.cppsrc/llmq/signing.cppsrc/llmq/signing.h
🧠 Learnings (19)
📓 Common learnings
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.
📚 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.cppsrc/llmq/signing_shares.hsrc/llmq/net_signing.cppsrc/Makefile.amsrc/llmq/context.cppsrc/llmq/signing.cppsrc/llmq/signing.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/init.cppsrc/llmq/signing_shares.hsrc/Makefile.amsrc/llmq/signing.cppsrc/llmq/signing.h
📚 Learning: 2025-10-02T18:29:54.756Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6840
File: src/net_processing.cpp:2882-2886
Timestamp: 2025-10-02T18:29:54.756Z
Learning: Across net_processing.cpp, once LLMQContext (m_llmq_ctx) is asserted non-null, its subcomponents (e.g., isman, qdkgsman, quorum_block_processor) are treated as initialized and used without extra null checks.
Applied to files:
src/init.cppsrc/llmq/net_signing.cppsrc/Makefile.amsrc/llmq/context.cppsrc/llmq/signing.cppsrc/llmq/signing.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} : LLMQ quorums must support multiple configurations (50/60, 400/60, 400/85) for different services (ChainLocks, InstantSend, governance voting)
Applied to files:
src/init.cppsrc/Makefile.amsrc/llmq/context.cppsrc/llmq/signing.cppsrc/llmq/signing.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/**/*.{cpp,h} : NodeContext must be extended with Dash-specific managers for each subsystem during initialization
Applied to files:
src/init.cpp
📚 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/llmq/net_signing.cppsrc/llmq/signing.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.amsrc/llmq/signing.cppsrc/llmq/signing.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} : ChainLocks implementation must prevent reorganizations and provide block finality through 51% attack prevention
Applied to files:
src/Makefile.amsrc/llmq/context.cppsrc/llmq/signing.cppsrc/llmq/signing.h
📚 Learning: 2025-01-02T21:50:00.967Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/context.cpp:42-43
Timestamp: 2025-01-02T21:50:00.967Z
Learning: LLMQContext manages concurrency for the `CInstantSendManager`. Previously, this was handled globally; now it's handled as a class member in `LLMQContext`, but the concurrency control remains consistent.
Applied to files:
src/llmq/context.cpp
📚 Learning: 2024-12-29T17:43:41.755Z
Learnt from: kwvg
Repo: dashpay/dash PR: 6504
File: src/llmq/quorums.cpp:224-224
Timestamp: 2024-12-29T17:43:41.755Z
Learning: The `CQuorumManager` is fully initialized by `LLMQContext`, addressing any concerns about the manager’s initialization sequence.
Applied to files:
src/llmq/context.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:
test/lint/lint-circular-dependencies.py
📚 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/llmq/signing.cpp
📚 Learning: 2025-07-15T14:53:04.819Z
Learnt from: knst
Repo: dashpay/dash PR: 6691
File: src/test/llmq_params_tests.cpp:148-151
Timestamp: 2025-07-15T14:53:04.819Z
Learning: In the Dash Core LLMQ implementation, signingActiveQuorumCount is never 0 in the actual parameters defined in params.h, making division by zero scenarios unrealistic in the max_cycles() function.
Applied to files:
src/llmq/signing.cppsrc/llmq/signing.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/llmq/signing.cpp
📚 Learning: 2025-08-19T15:08:00.835Z
Learnt from: knst
Repo: dashpay/dash PR: 6692
File: src/llmq/commitment.cpp:54-60
Timestamp: 2025-08-19T15:08:00.835Z
Learning: In Dash Core, llmq_params.size == 1 is used only for regtest environments, not on public networks, which significantly reduces the risk profile of code paths specific to single-member quorums.
Applied to files:
src/llmq/signing.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/node/chainstate.{cpp,h} : Chainstate initialization must be separated into dedicated src/node/chainstate.* files
Applied to files:
src/llmq/signing.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/llmq/signing.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,evo,coinjoin,governance}/**/*.{cpp,h} : Use unordered_lru_cache for efficient caching with LRU eviction in Dash-specific data structures
Applied to files:
src/llmq/signing.h
🧬 Code graph analysis (5)
src/llmq/signing_shares.h (1)
src/llmq/signing_shares.cpp (2)
TryRecoverSig(810-891)TryRecoverSig(810-811)
src/llmq/net_signing.cpp (2)
src/llmq/net_signing.h (1)
Start(33-35)src/llmq/signing.cpp (2)
ProcessRecoveredSig(477-516)ProcessRecoveredSig(477-477)
src/llmq/signing_shares.cpp (3)
src/validationinterface.cpp (2)
GetMainSignals(121-124)GetMainSignals(121-121)src/llmq/signhash.cpp (1)
SignHash(14-22)src/llmq/signhash.h (1)
SignHash(24-46)
src/llmq/signing.cpp (2)
src/llmq/signing.h (1)
CSigningManager(158-233)src/llmq/net_signing.cpp (2)
ProcessRecoveredSig(59-73)ProcessRecoveredSig(59-59)
src/llmq/signing.h (2)
src/llmq/signhash.h (2)
SignHash(24-46)hash(64-73)src/llmq/signing.cpp (8)
CSigningManager(329-333)CSigningManager(335-335)GetRecoveredSigForGetData(352-362)GetRecoveredSigForGetData(352-352)VerifyAndProcessRecoveredSig(364-396)VerifyAndProcessRecoveredSig(364-364)ProcessRecoveredSig(477-516)ProcessRecoveredSig(477-477)
⏰ 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: arm-linux-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_fuzz-build / Build source
- GitHub Check: linux64-build / Build source
- GitHub Check: mac-build / Build source
- GitHub Check: linux64_nowallet-build / Build source
- GitHub Check: linux64_tsan-build / Build source
🔇 Additional comments (20)
test/lint/lint-circular-dependencies.py (1)
26-28: Updated circular-dependency expectations match the new signing_shares/NetSigning topologyThe new cycles routed through
llmq/signing_sharesand the addedllmq/context/masternode/active/contextnodes look internally consistent with moving network concerns out ofllmq/signing; no unexpected new patterns stand out.Also applies to: 33-35, 54-56, 58-58, 63-63
src/init.cpp (1)
96-96: NetSigning integration with PeerManager mirrors existing NetInstantSend wiringIncluding
<llmq/net_signing.h>and addingNetSigningas an extra handler usingnode.llmq_ctx->sigmancleanly plugs recovered-signature networking into the existing handler mechanism without introducing new lifetime or ownership issues inAppInitMain.Also applies to: 2205-2207
src/Makefile.am (1)
276-276: Build rules correctly expose and compilellmq/net_signingAdding
llmq/net_signing.htoBITCOIN_CORE_Handllmq/net_signing.cpptolibbitcoin_node_a_SOURCESis consistent with other llmq components and ensures NetSigning is available wherever core headers and the node library are used.Also applies to: 539-539
src/llmq/context.cpp (1)
18-37: CSigningManager construction decoupled from ChainstateManager without changing LLMQContext semanticsConstructing
sigmanwith*qmananddb_paramsonly is in line with moving recovered‑sig handling toward a pure consensus component;ChainstateManagerremains wired intoquorum_block_processor,qman,clhandler, andisman, so LLMQContext’s external behaviour stays intact while removing the direct signing↔chainstate dependency.src/llmq/signing_shares.cpp (1)
102-133: SignHash-based session tracking is consistent and type-safeUsing
llmq::SignHashinInitSession,Session/SessionInfo, and the various QSIG* message handlers centralizes sign-hash computation and avoids ad‑hocuint256recomputations; conversions via.Get()and logging viaToString()are applied consistently where a raw hash or string is needed, so the change is behaviourally neutral while tightening types around session identity.Also applies to: 155-169, 353-371, 390-408, 420-472
src/llmq/net_signing.cpp (4)
19-35: LGTM - Missing return after misbehavior now addressed.The early return after
PeerMisbehavinghas been correctly added on line 31, preventing invalid signatures from being processed further.
37-57: Thread lifecycle management looks correct.The Start() and Stop() methods properly enforce the lifecycle contract: Start() prevents multiple worker threads, and Stop() requires Interrupt() to be called first. The use of assertions is appropriate for catching programming errors during development.
59-73: ProcessRecoveredSig implementation is correct.The method properly handles the processing flow: validate → notify listeners → emit main signals. The proactive relay logic excludes specific LLMQ types as intended.
75-136: Batch verification and misbehavior handling implemented correctly.The method properly:
- Fetches and processes reconstructed sigs
- Batches signature verification with appropriate security settings
- Handles invalid signatures by marking sources as bad
- Bans misbehaving peers
- Processes valid sigs with proper deduplication
The
nodeId == -1check on line 131 correctly identifies locally reconstructed signatures (as opposed to those received from peers) for proactive relay determination.src/llmq/signing.cpp (6)
329-332: Constructor refactoring aligns with separation goals.Removing the
CChainStatedependency successfully decouples the signing manager from chainstate, advancing the consensus/network separation objective.
364-396: Verification and queueing logic is sound.The method correctly:
- Validates quorum existence and activity
- Prevents duplicate processing via HasRecoveredSigForHash check
- Handles pending reconstructed signatures appropriately
- Queues validated signatures per node for batch verification
Thread safety is maintained with proper locking on line 387.
398-467: Refactored to return public keys instead of quorum objects.The updated signature returning
ret_pubkeys(map ofCBLSPublicKey) instead of quorum pointers is more efficient and reduces coupling. The validation logic correctly filters out missing or inactive quorums.
469-474: Efficient extraction of pending reconstructed signatures.Using
swapwithin the lock is an efficient pattern for extracting the entire map while minimizing lock contention.
477-516: Return value now indicates processing outcome.Changing the return type to
boolallows callers to determine whether the signature was newly processed (true) or already known/conflicting (false). The conflict detection and logging logic remains sound.
518-522: Thread-safe accessor for listeners.The method correctly returns a copy of the listener vector under lock, ensuring thread safety for callers.
src/llmq/signing.h (5)
16-16: Header dependency reduction through forward declaration.Adding the
SignHashforward declaration (line 37) instead of including the full header reduces compilation dependencies, improving build times.Also applies to: 37-37
55-55: Explicit return types improve clarity.Making
getLlmqType()return an explicitConsensus::LLMQType(line 55) improves code clarity and works well with the forward-declaredSignHashreturn type on line 69.Also applies to: 69-69
154-155: Listener interface preserved with future improvement noted.The TODO comment documents a potential future simplification of the return type. The current interface remains stable for this refactoring PR.
179-209: API surface updated for NetSigning integration.The new public methods are well-documented and properly annotated:
VerifyAndProcessRecoveredSigreplaces the old message processing interfaceFetchPendingReconstructed,CollectPendingRecoveredSigsToVerify,GetListeners, andProcessRecoveredSigsupport the new NetSigning workflow[[nodiscard]]and lock annotations improve safety and maintainability
211-216: Proper encapsulation with controlled access.Moving
GetDb()to the private section (line 213) with a friend declaration forCSigSharesManagerappropriately restricts database access while maintaining necessary functionality.Cleanup()correctly remains public for lifecycle management.Also applies to: 232-232
PastaPastaPasta
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 094b5a4
UdjinM6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 094b5a4
…rnance] a67300c fix: multiple CI failures (Konstantin Akimov) be62366 refactor: pass connman in constructor of NetHandler's child which need it (Konstantin Akimov) dc08ef4 refactor: break circular dependency of CMasternodeManager over net.h (Konstantin Akimov) 9ac269c refactor: move ProcessMessage from CMasternodeSync to SyncManager (Konstantin Akimov) b9aec50 refactor: split NetGovernance to SyncManager and NetGovernance (Konstantin Akimov) 106827c fmt: clang-format (Konstantin Akimov) fc21e51 refactor: move ProcessMessage from CGovernanceManager to NetGovernance (Konstantin Akimov) fa0e173 refactor: add CConnman to NetHandler's ProcessMessage (Konstantin Akimov) 7f3d16d chore: apply clang-format and adjust logs (Konstantin Akimov) 461392e refactor: move RequestGovernanceObjectVotes to NetGovernance to drop dependency governance on net_processing (Konstantin Akimov) 8425ca1 fix: reduce scope of ::cs_main during PeerGetRequestedObjectCount (Konstantin Akimov) 77faacc refactor: split FetchGovernanceObjectVotes to own member of CGovernanceManager (Konstantin Akimov) e7f9218 refactor: drop a wrapper RequestGovernanceObjectVotes for a single node (Konstantin Akimov) eb9607e refactor: move DEFAULT_SYNC_MEMPOOL form validation.h to masternode/sync.h (Konstantin Akimov) be0be6b chore: fix clang-format and adjust log messages (Konstantin Akimov) e878592 refactor: drop dependency of masternode/sync on governance by moving relevant code to NetGovernance (Konstantin Akimov) 7e4849a refactor: inline method CleanOrphanObjects to CGovernanceManager::RequestOrphanObjects (Konstantin Akimov) 285def6 refactor: use NetHandler to move scheduler & partially network related code from governance (Konstantin Akimov) 78c6062 refactor: drop unused includes and forward declarations from masternode/sync and governance/governance (Konstantin Akimov) 91129d4 refactor: drop dead code with ERROR_TXINDEX_DISABLED_WHEN_GOV_ENABLED (Konstantin Akimov) Pull request description: ## 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`. `CMasternodeSync` is refactored to pure state-machine without dependencies on net.h. 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: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: light ACK a67300c Tree-SHA512: dc8548268cd3156848e50fd338776be676c63d966fd6b576fb1f0f3b5416afd6d9b4a4d17f6df7cd1cf3b0c4f8908b4848e196dad808f430207d75e2adf6bbe0
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
llmq::CSigningManageronPeerManager(llmq::CSigSharesManageris out of scope; will be done in the PR 2b/N).It is a split from proof-of-concept PR #6934
Prior work: #6959
How Has This Been Tested?
Run unit & functional tests.
Run a linter
test/lint/lint-circular-dependencies.pyOverall conception is tested in #6934 as potentially successful to finish separation of network & consensus code.
Breaking Changes
N/A
Checklist: