bch(M5): HeaderChain back-off locator for IBD getheaders continuation - #208
Merged
Conversation
…tion The headers-first IBD continuation (ContinueSync) re-issued getheaders with a single-hash locator anchored at the last learned header. A lone hash is not fork-resilient: if our latest header sits on a minority fork the peer cannot anchor, the peer returns nothing and IBD stalls silently with no common-ancestor fallback. Route the continuation through the authoritative HeaderChain locator (get_locator(), exponential back-off from the tip) via an injected provider on NodeP2P, wired by EmbeddedDaemon after start_p2p(). The header chain has already ingested the batch (wire_chain_ingest) before ContinueSync fires, so the locator is anchored at the just-learned tip AND carries deep back-off references. Falls back to the single-hash anchor only when no provider is wired (degraded). Selection logic extracted to header_sync::choose_continue_locator (pure, templated) and unit-tested alongside the classify policy. Pure SPV wire-sync: no PoW/share/coinbase/AuxPoW/PPLNS surface; src/impl/bch/ only.
…e single-hash stalls The progress soak drove cold-start IBD on a clean chain; header_sync_test pinned choose_continue_locator as a pure function. Neither exercised the COMPOSITIONAL property PR #208 actually buys: that the IBD *loop*, driven through choose_continue_locator, CONVERGES to the peer tip when our latest header sits on a minority fork -- the silent-stall case the back-off locator fixes. Add three loop-level invariants over a live-shaped cursor (anchor 955700 -> peer tip 955822, the VM300 bchn-bch range), modeling the real BIP31 back-off (get_locator_internal) and a peer that anchors only at a locator entry on its main chain: inv.6 shallow fork: back-off locator anchors at the EXACT common ancestor, converges, re-walks ZERO already-held headers (false_evict analog == 0). inv.7 same fork, degraded single-hash fallback (no provider) STALLS and never converges -- proves the provider is load-bearing, not cosmetic. inv.8 deep 50-block fork still converges; re-walk bounded by one batch. Header-only over coin/header_sync.hpp; src/impl/bch/test/ only; impl_bch stays unregistered (bch skip-green). p2pool-merged-v36 surface: NONE.
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.
M5 embedded-daemon body, pure src/impl/bch/ SPV wire-sync.
PROBLEM: the headers-first IBD continuation (ContinueSync) re-issued getheaders with a single-hash locator anchored at the last learned header. A lone hash is not fork-resilient — if our tip sits on a minority fork the peer cannot anchor, the peer returns nothing and IBD stalls silently (no common-ancestor fallback).
FIX: route the continuation through the authoritative HeaderChain locator (get_locator(), exponential back-off) via an injected provider on NodeP2P, wired by EmbeddedDaemon after start_p2p(). HeaderChain has already ingested the batch (wire_chain_ingest) before ContinueSync fires, so the locator is anchored at the just-learned tip AND carries deep back-off references. Degraded single-hash fallback retained when no provider is wired.
Selection logic extracted to header_sync::choose_continue_locator (pure, templated) and unit-tested alongside classify policy — header_sync_test: ALL PASS. Both touched headers syntax-clean (-std=c++20).
SURFACE: p2pool-merged-v36 = NONE (no PoW/share/coinbase/AuxPoW/PPLNS). Per-coin isolation: src/impl/bch/ only; no prefix/identifier/consensus value; no bitcoin_family. Auto-merge-lane eligible; merge operator-gated.