From f69f08792a7cf82c243d885d4e513254191b1f91 Mon Sep 17 00:00:00 2001 From: Sami Ahmed Date: Fri, 31 Jul 2026 20:02:15 -0700 Subject: [PATCH] [grade=B] fix(staking): carve out caught-up nodes from IBD gate so chain can self-heal IsStakingSafe() refused to stake whenever IsInitialBlockDownload() was true, and IBD flips true whenever the chain tip is older than 24h. After 24h of no blocks, every node simultaneously refuses to stake and the network deadlocks. Narrow the gate: only refuse when IBD is true AND the local height is behind the peer/checkpoint estimate. A node at the peer median clears the gate and keeps staking through idle periods, so the chain can restart itself. Genuinely-behind nodes still hold off. Block validation, reorg rules, and checkpoint rules unchanged. The existing -forcestaking bootstrap escape hatch still works on nodes caught up to the checkpoint. Codex verdict: urn:ump:6brctfzo5mrplzpyolstra5ula3hwtcy6t7bdd2iey552ozsoeiq --- src/main.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9a54f2e..806c572 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1650,8 +1650,16 @@ int GetNumBlocksOfPeers() bool IsStakingSafe(const CWallet* pwallet, const std::vector& vNodesSnapshot) { - // (1) Never stake during IBD. - if (IsInitialBlockDownload()) + // (1) Never stake during IBD UNLESS we're caught up to peers. A node + // that is fully synced but idle (chain stalled >24h, so IBD flips + // true via the stale-tip heuristic) MUST keep staking so the network + // can self-heal. Without this carve-out, every node simultaneously + // refuses to stake after 24h of no blocks and the chain deadlocks. + // + // GetNumBlocksOfPeers() is the peer median height clamped to the + // checkpoint estimate, so this comparison is approximate: a node at + // the peer median clears it, a node behind does not. + if (IsInitialBlockDownload() && nBestHeight < GetNumBlocksOfPeers()) { if (fDebug) printf("STAKING-GATE: refuse (IBD)\n"); return false;