feat(warmboot): wire g_warm_boot_mismatch — detect a provably non-canonical tip#50
Merged
Eth-Interchained merged 1 commit intoJun 25, 2026
Conversation
…onical tip
g_warm_boot_mismatch was declared, externed, and reset on every TryWarmBoot, but
NEVER set anywhere — a dead flag whose header comment ("reserved for active fork
detection") described behavior that did not exist. The warm-boot state machine
therefore had only two outcomes: seam verified (proceed), or the 2-minute
watchdog timeout -> nedb_warmboot_unconfirmed -> full scan next start. A genuinely
orphaned/forked tip was indistinguishable from a merely slow/behind seed.
Wire it. In ProcessHeadersMessage, after the peer's headers are accepted and
pindexLast is known (under cs_main), detect the negative of the existing seam
check: if a peer presents a strictly-MORE-work valid header chain
(pindexLast->nChainWork > g_warm_boot_tip_chainwork) whose block AT our warm-boot
tip height is a different hash (pindexLast->GetAncestor(tip_height) != our tip),
then our tip is provably NOT on the most-work chain. Set g_warm_boot_mismatch,
log it distinctly, and persist nedb_warmboot_unconfirmed immediately via the new
WarmBootMarkUnconfirmed() so the next (controlled) startup full-scans from 0.
Conservative by construction:
- Requires STRICTLY more work, so a weaker/equal fork never demotes our tip.
- GetAncestor() returns null if the peer's chain is not linked down to our tip
height -> no conclusion drawn (absence of evidence remains the watchdog's job).
- No live wipe or reorg here: matches the watchdog doctrine that destructive
action belongs at the controlled startup path. Mismatch acts on POSITIVE proof
immediately instead of waiting out the 2-minute watchdog.
- Worst case of a false positive is one slow full-scan boot (non-destructive;
immutable objects preserved), and forging strictly-more valid PoW work is not
cheap.
Adds g_warm_boot_tip_chainwork (the persisted cumulative work of the warm-boot
tip) so the seam can compare a peer's chain work without an index lookup.
Tests: NO C++ build harness in the authoring sandbox (no g++/cmake), so this is
source-reviewed only and MUST be built + reviewed before merge. This is the
network-anchoring path, not consensus validation. Suggested validation: a node
warm-booted on an orphaned tip should log "WarmBoot MISMATCH" on the first
strictly-more-work peer headers that fork below its tip height and set the
unconfirmed flag; a node on the canonical tip must never trip it (the seam closes,
or GetAncestor matches our tip). Review-only — do not auto-merge.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eth-Interchained
pushed a commit
that referenced
this pull request
Jun 25, 2026
…coin prefetch) main advanced past this branch's base via #52 (batch-prefetch block input coins, 867ffbd) and #50 (warm-boot seam tip-mismatch detection, 24d137a). Conflicts were confined to the warm-boot state block: - validation.h / validation.cpp: keep main's authoritative g_warm_boot_mismatch (the real ProcessHeadersMessage detection from #50) + g_warm_boot_tip_chainwork + WarmBootMarkUnconfirmed(); add this branch's g_warm_boot_anchor alongside. This branch predated #50 and carried only a 'reserved' mismatch stub -- dropped in favor of #50's implementation. Semantic reconciliation (the #50 x #51 interaction): - net_processing.cpp ProcessHeadersMessage: #50's mismatch-detection gate was written before -anchor existed and lacked the anchor guard. Added !g_warm_boot_anchor.load() to it, mirroring the seam-verify gate above. A declared root-of-trust seed must never let a peer's more-work header chain flag its own canonical tip for resync -- that would be a DoS on exactly the nodes -anchor protects. This is the only behavioral change the merge introduces. init.cpp (-anchor arg beside #52's -coinprefetch, the warm_ok anchor set, and the ThreadImport startup-ABC gate) auto-merged cleanly and was verified by hand. Tests: no C++/cargo harness in the authoring sandbox -- source-reviewed; CI (Codemagic) compiles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wires
g_warm_boot_mismatch— a flag that was declared, externed, and reset on everyTryWarmBootbut never set anywhere. Its header comment ("reserved for active fork detection") described behavior that didn't exist, so a genuinely orphaned tip was indistinguishable from a merely slow/behind seed (both fell through the same 2-minute watchdog ->nedb_warmboot_unconfirmed-> full scan next start).What it does now
In
ProcessHeadersMessage, afterpindexLastis accepted (undercs_main), detect the negative of the existing Proof-of-Prefix seam: if a peer presents a strictly-more-work valid header chain (pindexLast->nChainWork > g_warm_boot_tip_chainwork) whose block at our warm-boot tip height is a different hash (GetAncestor(tip_height) != our tip), then our tip is provably not on the most-work chain. Setg_warm_boot_mismatch, log distinctly, and persistnedb_warmboot_unconfirmedimmediately (newWarmBootMarkUnconfirmed()), so the next controlled startup full-scans from 0 — instead of waiting out the 2-minute watchdog.Why this matters
Distinguishes "provably wrong tip" (act now) from "unconfirmed / seed behind" (wait) — the gap GPT-5.5 flagged. Closes a dead defensive flag whose comment lied.
Conservative by construction
GetAncestor()null (peer chain not linked down to our tip height) -> no conclusion (absence of evidence stays the watchdog's job).Engine impact
None — itcd-side only; the NEDB engine repo is untouched.
Studio impact
None.
Tests run
None — no g++/cmake in the authoring sandbox; source-reviewed only. This is the network-anchoring path (not consensus validation). Please build on CI/iMac and review before merge. Suggested validation: a node warm-booted on an orphaned tip logs
WarmBoot MISMATCHon the first strictly-more-work peer headers that fork below its tip height and sets the unconfirmed flag; a node on the canonical tip never trips it.Follow-up
Review-only — do not auto-merge.
© Interchained LLC × Claude