ltc(v36): advertise raw head pre-sync + re-advert with 10s timer (root-2, complete) - #104
Merged
Merged
Conversation
…ert) On a --genesis node whose verified chain is still empty at the moment of a version handshake, best_share_hash() returns NULL, so the peer never calls download_shares() and the broadcast path cannot backfill (our head shares are already de-dup-marked in m_shared_share_hashes). The two sides deadlock with Peers connected but zero share exchange. Decouple advertisement from the verified chain: - advertised_best_share(): peer-facing only (version handshake + timer re-announce). Returns the verified head when present, else the tallest RAW head. Never used for work/share creation, so it cannot cause us to build on an unagreed chain (best_share_hash() still owns that path unchanged). - send_version() now advertises advertised_best_share(). - readvertise_best_share(): re-push the tip walk to all peers bypassing the de-dup set. Fired from run_think() on best-change, and once on a 10s one-shot timer when the verified chain first becomes non-empty, so a peer that handshook during the empty window can still ingest our chain. All advertise paths use the established try_to_lock discipline; the IO thread never blocks on m_tracker_mutex.
frstrtr
added a commit
that referenced
this pull request
Jun 16, 2026
Mirror the LTC timer leg from #104/e01c1606. The event leg (readvertise_best on think work-refresh + clean_tracker best-change) goes dead exactly when think() wedges -- the livelock-adjacent scenario this fix family targets. Arm a one-shot core::Timer(10s) -> readvertise_best() in the run_think IO-phase, fired exactly once when the verified chain first becomes non-empty, so a peer that handshook during the empty window can still ingest our chain even if a later think() cycle wedges. Composes with the merged #97 think-watchdog: timer runs on the IO thread, pure reads, broadcast stays try_to_lock, no new mutex.
frstrtr
added a commit
that referenced
this pull request
Jun 16, 2026
…r leg Mirror the LTC timer leg from #104/e01c1606. The event leg (readvertise_best on think work-refresh + clean_tracker best-change) goes dead exactly when think() wedges -- the livelock-adjacent scenario this fix family targets. Arm a one-shot core::Timer(10s) -> readvertise_best() in the run_think IO-phase, fired exactly once when the verified chain first becomes non-empty, so a peer that handshook during the empty window can still ingest our chain even if a later think() cycle wedges. Composes with the merged #97 think-watchdog: timer runs on the IO thread, pure reads, broadcast stays try_to_lock, no new mutex. Co-authored-by: frstrtr <frstrtr@users.noreply.github.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.
Root-2: genesis-null head advertisement — COMPLETE fix (supersedes #103)
This is the canonical Root-2 fix (commit e01c160, GPG-signed). It supersedes PR #103 (head
ltc-doge/root2-genesis-null-advert@ b56bfb2), which carried only the on-think / best-change re-advert — a half-fix that regresses whenthink()is wedged. Recommend closing #103 in favour of this PR. LTC only; BTC mirror tracked separately by btc-heap-opt.(a) Bug fixed — genesis-null-advert
On a fresh (
--genesis) LTC node the verified share set is empty at the version handshake.best_share_hash()is verified-only and collapses touint256::ZEROonce peers exist (so work/template never builds on a MAX_TARGET head), so the node advertised ZERO insend_version()→ the peer never issueddownload_shares()→ the chain failed to propagate.Fix:
send_version()now advertises viaadvertised_best_share()(raw head: think's current best, else tallestm_chainhead, else ZERO at true genesis).best_share_hash()stays verified-only for work/template selection — unchanged.(b) Re-advertisement — two legs, think-independent
readvertise_best_share()re-announces on think work-refresh and on best-change (5-deep tip walk, de-dup bypass,try_to_lock, skips rejected shares).m_readvert_timer(core::Timer) is armed lazily once the verified chain first becomes non-empty and firesreadvertise_best_share()on a 10s cadence, plus a one-shot delayed re-advert on first non-empty verified chain. This covers the case the event leg cannot — a peer that connects during a long quiescent gap with no head change, and re-advert progress even ifthink()is wedged.(c) Timer — IMPLEMENTED, not deviated
Unlike #103 (which deviated to think-cycle cadence only), this branch implements the explicit ~10s re-advert timer the spec called for. The timer arms inside the run_think IO-phase and composes with the already-merged PR #97 lock-yield + think-watchdog (ee906e8), which guarantees think progress → arm.
Livelock-safety — confirmed, no new exclusive lock
No new mutex.
advertised_best_share()/readvertise_best_share()are reads; the broadcast path keepstry_to_lock(non-blocking), so nothing on the compute-thread hot path can reintroduce the phase2 livelock signature. +93/-1 node.cpp, +21 node.hpp.