Skip to content

fix: initialize quorum connections on startup#7240

Open
UdjinM6 wants to merge 4 commits intodashpay:developfrom
UdjinM6:fix/initialize-quorum-connections-on-startup
Open

fix: initialize quorum connections on startup#7240
UdjinM6 wants to merge 4 commits intodashpay:developfrom
UdjinM6:fix/initialize-quorum-connections-on-startup

Conversation

@UdjinM6
Copy link

@UdjinM6 UdjinM6 commented Mar 21, 2026

Issue being fixed or feature implemented

After PR #7063 (commit 1360d9d), CQuorumManager::UpdatedBlockTip was moved from CDSNotificationInterface::UpdatedBlockTip (which is called directly at startup via InitializeCurrentBlockTip) to the ActiveContext/ObserverContext CValidationInterface subscribers. These subscribers only receive UpdatedBlockTip via GetMainSignals() broadcast, which is never triggered at startup on idle chains — ActivateBestChain early-returns when pindexMostWork == m_chain.Tip().

This means that after a full restart with no new blocks:

  • QuorumObserver::UpdatedBlockTip never fires
  • CheckQuorumConnections is never called
  • Quorum connections are never re-established
  • MNs cannot exchange signing shares
  • InstantSend and ChainLock signing fails

What was done?

Fix by adding InitializeCurrentBlockTip() to ActiveContext and ObserverContext, called from the loadblk thread right after CDSNotificationInterface::InitializeCurrentBlockTip(). This method also calls the new QuorumObserver::InitializeQuorumConnections(), which bypasses the IsBlockchainSynced() guard (mnsync is not yet complete at that point in the startup sequence).

How Has This Been Tested?

See new "IS after MNs restart" test which fails on develop and passes with the patch applied

Breaking Changes

n/a

Checklist:

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

UdjinM6 and others added 2 commits March 21, 2026 19:05
After PR dashpay#7063 (commit 1360d9d), CQuorumManager::UpdatedBlockTip was
moved from CDSNotificationInterface::UpdatedBlockTip (which is called
directly at startup via InitializeCurrentBlockTip) to the
ActiveContext/ObserverContext CValidationInterface subscribers. These
subscribers only receive UpdatedBlockTip via GetMainSignals() broadcast,
which is never triggered at startup on idle chains — ActivateBestChain
early-returns when pindexMostWork == m_chain.Tip().

This means that after a full restart with no new blocks:
- QuorumObserver::UpdatedBlockTip never fires
- CheckQuorumConnections is never called
- Quorum connections are never re-established
- MNs cannot exchange signing shares
- InstantSend and ChainLock signing fails

Fix by adding InitializeCurrentBlockTip() to ActiveContext and
ObserverContext, called from the loadblk thread right after
CDSNotificationInterface::InitializeCurrentBlockTip(). This method
also calls the new QuorumObserver::InitializeQuorumConnections(),
which bypasses the IsBlockchainSynced() guard (mnsync is not yet
complete at that point in the startup sequence).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests that InstantSend works after a full cluster restart without
mining any new blocks. This exercises the quorum connection
re-establishment path added in the previous commit.

The test:
- Funds a sender, chainlocks the tip
- Restarts all nodes (simple nodes and masternodes)
- Reconnects all nodes to node 0
- Bumps mocktime past WAIT_FOR_ISLOCK_TIMEOUT
- Sends a transaction and verifies it gets an IS lock

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@UdjinM6 UdjinM6 added this to the 24 milestone Mar 21, 2026
@github-actions
Copy link

github-actions bot commented Mar 21, 2026

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c25c61a8d9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@UdjinM6 UdjinM6 changed the title Fix/initialize quorum connections on startup fix: initialize quorum connections on startup Mar 21, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 95816a97-b7e4-425f-876c-d83642dc473e

📥 Commits

Reviewing files that changed from the base of the PR and between 62b970c and 5a43003.

📒 Files selected for processing (2)
  • src/init.cpp
  • test/functional/p2p_instantsend.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/init.cpp
  • test/functional/p2p_instantsend.py

Walkthrough

Adds explicit initialization entry points for the current block tip: InitializeCurrentBlockTip(const CBlockIndex* tip, bool ibd) is added to ActiveContext and ObserverContext. Each implementation calls UpdatedBlockTip(tip, nullptr, ibd) and, when tip is non-null, invokes qman_handler->InitializeQuorumConnections(tip). QuorumObserver gains InitializeQuorumConnections(...) which iterates configured LLMQs and calls CheckQuorumConnections per quorum. init.cpp now calls the new initializer(s) after obtaining chainman.ActiveTip() and IBD state. A functional test was extended to verify InstantSend behavior after node/masternode restarts.

Sequence Diagram(s)

sequenceDiagram
    participant AppInitMain as AppInitMain (init.cpp)
    participant ChainMan as ChainManager
    participant ActiveCtx as ActiveContext / ObserverContext
    participant QMan as QuorumManager (qman_handler) 
    participant QObserver as QuorumObserver

    AppInitMain->>ChainMan: ActiveTip() / IsInitialBlockDownload()
    ChainMan-->>AppInitMain: tip, ibd

    AppInitMain->>ActiveCtx: InitializeCurrentBlockTip(tip, ibd)
    activate ActiveCtx
    ActiveCtx->>ActiveCtx: UpdatedBlockTip(tip, nullptr, ibd)
    alt tip != nullptr
        ActiveCtx->>QMan: InitializeQuorumConnections(tip)
        activate QMan
        QMan->>QObserver: InitializeQuorumConnections(tip)
        activate QObserver
        QObserver->>QObserver: for each llmq: CheckQuorumConnections(params, tip)
        deactivate QObserver
        deactivate QMan
    end
    deactivate ActiveCtx
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: initialize quorum connections on startup' accurately and concisely summarizes the main change: adding initialization of quorum connections during startup to fix InstantSend/ChainLock failures after node restart.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the problem (quorum connections not re-established on restart), the solution (adding InitializeCurrentBlockTip methods), and testing approach (new restart test).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

Clean, well-targeted fix for a real regression where quorum connections were not re-established after a full restart on idle chains. The new InitializeQuorumConnections method correctly bypasses the IsBlockchainSynced() guard, and the functional test directly exercises the fix scenario. No blocking issues found; two minor suggestions for code hygiene and test robustness.

Reviewed commit: c25c61a

🟡 2 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/active/context.cpp`:
- [SUGGESTION] lines 100-106: Identical InitializeCurrentBlockTip bodies in ActiveContext and ObserverContext
  The method body is identical in ActiveContext (lines 100-106) and ObserverContext (src/llmq/observer/context.cpp:50-56): both call UpdatedBlockTip then qman_handler->InitializeQuorumConnections. The differentiated behavior comes from two layers: (1) UpdatedBlockTip itself differs — ActiveContext also calls nodeman, ehf_sighandler, gov_signer — and (2) InitializeQuorumConnections dispatches through the virtual CheckQuorumConnections (QuorumParticipant overrides QuorumObserver). Both classes are `final` and share only CValidationInterface as a base, so extracting a common implementation isn't straightforward. Acceptable duplication for now, but worth noting if more initialization steps are added.

In `test/functional/p2p_instantsend.py`:
- [SUGGESTION] line 199: Post-restart IS lock only verified on sender; verifying on receiver would strengthen the test
  The pre-restart funding TX verifies IS locks on all nodes (lines 157-158), but the post-restart TX only checks the sender (line 199). Since the purpose of this test is to confirm quorum signing infrastructure works across the cluster after restart, verifying the lock propagates to at least the receiver would provide stronger assurance.

Comment on lines +100 to +106
void ActiveContext::InitializeCurrentBlockTip(const CBlockIndex* tip, bool ibd)
{
UpdatedBlockTip(tip, nullptr, ibd);
if (tip) {
qman_handler->InitializeQuorumConnections(tip);
}
}

Choose a reason for hiding this comment

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

🟡 Suggestion: Identical InitializeCurrentBlockTip bodies in ActiveContext and ObserverContext

The method body is identical in ActiveContext (lines 100-106) and ObserverContext (src/llmq/observer/context.cpp:50-56): both call UpdatedBlockTip then qman_handler->InitializeQuorumConnections. The differentiated behavior comes from two layers: (1) UpdatedBlockTip itself differs — ActiveContext also calls nodeman, ehf_sighandler, gov_signer — and (2) InitializeQuorumConnections dispatches through the virtual CheckQuorumConnections (QuorumParticipant overrides QuorumObserver). Both classes are final and share only CValidationInterface as a base, so extracting a common implementation isn't straightforward. Acceptable duplication for now, but worth noting if more initialization steps are added.

source: ['claude']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/active/context.cpp`:
- [SUGGESTION] lines 100-106: Identical InitializeCurrentBlockTip bodies in ActiveContext and ObserverContext
  The method body is identical in ActiveContext (lines 100-106) and ObserverContext (src/llmq/observer/context.cpp:50-56): both call UpdatedBlockTip then qman_handler->InitializeQuorumConnections. The differentiated behavior comes from two layers: (1) UpdatedBlockTip itself differs — ActiveContext also calls nodeman, ehf_sighandler, gov_signer — and (2) InitializeQuorumConnections dispatches through the virtual CheckQuorumConnections (QuorumParticipant overrides QuorumObserver). Both classes are `final` and share only CValidationInterface as a base, so extracting a common implementation isn't straightforward. Acceptable duplication for now, but worth noting if more initialization steps are added.

UdjinM6 and others added 2 commits March 22, 2026 02:46
Move InitializeCurrentBlockTip for active_ctx (masternode mode) to
after nodeman->Init() so that GetProTxHash() is guaranteed to be set
before EnsureQuorumConnections runs.

Currently this works by accident: UpdatedBlockTip calls Init()
internally when the MN state is not READY. But this is a non-obvious
side effect and would silently break quorum connections if someone
refactored UpdatedBlockTip to add an early return. Also, if
IsInitialBlockDownload() is true, UpdatedBlockTip returns early and
Init() never runs, leaving a null proTxHash for quorum setup.

Making the dependency explicit improves robustness without changing
observable behavior in normal operation.

Also add a MN-to-MN quorum connection assertion in the restart test
to verify that quorum connections form between masternodes after
restart, not just that IS locks work (which can succeed via
concentrated sigshare sending over any authenticated connection).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@UdjinM6 UdjinM6 force-pushed the fix/initialize-quorum-connections-on-startup branch from 62b970c to 5a43003 Compare March 21, 2026 23:47
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a430037bd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

Automated code review.

Reviewed commit: 5a43003

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants