Skip to content

Commit ecd5c90

Browse files
UdjinM6claude
andcommitted
refactor: defer active_ctx block tip init until after nodeman->Init()
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>
1 parent c25c61a commit ecd5c90

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

src/init.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,11 +2420,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
24202420
{
24212421
const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman.ActiveTip());
24222422
const bool ibd = chainman.ActiveChainstate().IsInitialBlockDownload();
2423-
if (node.active_ctx) {
2424-
node.active_ctx->InitializeCurrentBlockTip(tip, ibd);
2425-
} else if (node.observer_ctx) {
2423+
if (node.observer_ctx && !node.active_ctx) {
24262424
node.observer_ctx->InitializeCurrentBlockTip(tip, ibd);
24272425
}
2426+
// Note: active_ctx initialization is deferred until after nodeman->Init()
2427+
// so that GetProTxHash() is available for quorum connection setup.
24282428
}
24292429

24302430
{
@@ -2490,6 +2490,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
24902490

24912491
if (node.active_ctx) {
24922492
node.active_ctx->nodeman->Init(chainman.ActiveTip());
2493+
// Initialize current block tip after nodeman->Init() so that
2494+
// GetProTxHash() is available for quorum connection setup.
2495+
// Without this ordering, EnsureQuorumConnections returns early
2496+
// because the null proTxHash makes the MN appear as a non-member.
2497+
const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman.ActiveTip());
2498+
const bool ibd = chainman.ActiveChainstate().IsInitialBlockDownload();
2499+
node.active_ctx->InitializeCurrentBlockTip(tip, ibd);
24932500
}
24942501
});
24952502
#ifdef ENABLE_WALLET

test/functional/p2p_instantsend.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,10 @@ def test_instantsend_after_restart(self):
174174
for mn_info in self.mninfo:
175175
self.start_masternode(mn_info)
176176

177-
# reconnect: simple nodes to node 0, MNs to node 0
178-
# quorum connections are re-established automatically via InitializeCurrentBlockTip
177+
# reconnect: simple nodes to node 0, MNs to node 0 only.
178+
# Quorum connections between MNs must be re-established automatically
179+
# via InitializeCurrentBlockTip → EnsureQuorumConnections, NOT via
180+
# manual connect_nodes between MN pairs.
179181
for i in range(1, num_simple_nodes):
180182
self.connect_nodes(i, 0)
181183
for mn_info in self.mninfo:
@@ -188,6 +190,22 @@ def test_instantsend_after_restart(self):
188190
self.bump_mocktime(10 * 60 + 1)
189191
self.sync_blocks()
190192

193+
# Verify that MNs formed quorum connections to other MNs after restart.
194+
# InitializeCurrentBlockTip → EnsureQuorumConnections must populate
195+
# masternodeQuorumNodes so ThreadOpenMasternodeConnections establishes
196+
# MN-to-MN links beyond the manual connections to node 0.
197+
self.log.info("Verifying MN-to-MN quorum connections formed after restart")
198+
for mn_info in self.mninfo:
199+
mn_node = self.nodes[mn_info.nodeIdx]
200+
201+
def check_mn_peers(node=mn_node, my_hash=mn_info.proTxHash):
202+
peers = node.getpeerinfo()
203+
mn_peers = set(p['verified_proregtx_hash'] for p in peers
204+
if p.get('verified_proregtx_hash', '') != '')
205+
other_mn_peers = mn_peers - {my_hash}
206+
return len(other_mn_peers) > 0
207+
self.wait_until(check_mn_peers, timeout=30)
208+
191209
# re-grab references after restart
192210
sender = self.nodes[self.sender_idx]
193211
receiver = self.nodes[self.receiver_idx]

0 commit comments

Comments
 (0)