btc(v36): livelock mirror — lock-yield + think() watchdog (scaffold) - #97
Merged
Conversation
frstrtr
commented
Jun 15, 2026
Owner
Mirror of the ltc-doge-locked livelock fix shape onto the BTC variant. Branch-only scaffold; soak freeze in effect, deploy operator-gated. PRIMARY (lock-yield / bound think): extend the existing cooperative-yield (needs_continue) mechanism into the v36 decayed-cumulative-weight + PPLNS scoring walk so the compute thread periodically releases m_tracker_mutex, drain_pending_adds() runs, and a continuation is re-posted — breaking the IO-thread starvation. Enforced at a COARSE per-scored-head boundary (THINK_WALK_YIELD_BUDGET) sized far above a healthy full-window pass so it never trips on normal load. Exact intra-walk yield point + optional zero-divisor guard (degenerate target_ratio==0) left as a marked TODO at get_decayed_cumulative_weights, pending ltc-doge PIE-core symbolization. SECONDARY (defense-in-depth) think() watchdog: IO-thread steady_timer that NEVER touches m_tracker_mutex; on a >THINK_WATCHDOG_SECONDS cycle it dumps the stack, clears the deadline, and resets m_think_running so the pipeline recovers. Atomic deadline + generation counter guard against late/dup fires. MAX_PENDING_ADDS caps the deferred queue with backpressure (drop + warn) so a wedged think() cannot grow memory unbounded. Touches the try-lock-with-defer twin sites (node.cpp:395 defer path). Builds clean: c2pool-btc links (EXIT=0).
…nodiscard) std::shared_lock::try_lock() is [[nodiscard]]; the guard previously called it then re-derived the outcome via owns_lock(), so the return value was dropped and -Wunused-result -> -Werror failed btc smoke (node.cpp). Assign the return directly to ok_ — the same boolean that drives operator bool() and the IO-thread yield/defer decision — instead of discarding it. Mirrored in ltc/node.hpp to keep the shared TrackerReadGuard idiom identical.
CI Boost.ASIO (conan2 boostdcfa654164f57) lacks the deprecated cancel(error_code&) overload; only zero-arg cancel() (returns size_t, noexcept) exists. Switch to it and drop the local error_code. ltc mirror unaffected: it has no watchdog_timer; its only cancel() is the zero-arg socket cancel in coin/p2p_connection.hpp.
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 was referenced Jun 16, 2026
frstrtr
added a commit
that referenced
this pull request
Jun 21, 2026
…60%-work switch rule Completes the mint<->accept coupling. The prior commit (27a4463) made the AutoRatchet MINT guard work-weighted, but BTC share_check step 2 still admitted a version BOUNDARY via the non-canonical 95%-flat-COUNT should_punish_version path -- the inverse of the #97 LTC wedge: a work-weighted mint guard in front of a flat-count accept gate. Port the canonical accept rule from ltc/share_check.hpp ~1715-1762 (p2pool data.py check() 1396-1414): a boundary share (share.version != parent.version) is valid only when, in the window [CHAIN_LENGTH*9/10, CHAIN_LENGTH] behind the PARENT, the new version holds >= 60% of the PPLNS-WORK-WEIGHTED desired-version tally (get_desired_version_weights). same-version always valid; -1 downgrade valid (AutoRatchet V35-after-V36); any other jump throws "invalid version jump". Removes the should_punish_version call from the accept path entirely (F10/(b): the 60% switch rule is now the ONLY version gate, matching ltc/dgb). Per-coin lane, src/impl/btc/ only. Semantics byte-mirror LTC. Merge held pending full gh CI rollup CLEAN + BTC crossing re-soak; no self-merge.
frstrtr
added a commit
that referenced
this pull request
Jun 21, 2026
…pling ltc: couple AutoRatchet mint gate to the work-weighted accept gate (fix #97 crossing wedge)
frstrtr
added a commit
that referenced
this pull request
Jun 21, 2026
) * btc: couple AutoRatchet mint gate to the work-weighted accept gate Mirror of the LTC mint<->accept coupling fix onto the BTC lane. The v35->v36 crossing soak (#97) wedged on LTC: AutoRatchet activated on a 95%-by-flat-COUNT desired-version tally, but the consensus accept gate (share_check step 2 / p2pool check() data.py:1399) admits a V36 boundary share only when desired-version reaches 60% by WORK over the [9/10*CL, CL] window -- canonical get_desired_version_counts (data.py:2651) weights each share by target_to_average_attempts(target). Under heterogeneous hashrate a small miner set can carry the COUNT past 95% while the work-weighted tail sits below 60%, so the activated node mints a V36 boundary share that every peer rejects, freezing the crossing. btc/auto_ratchet.hpp carried the byte-identical flat-count tail guard, so the same latent wedge existed on this SHA256d sharechain. Switch the tail guard from get_desired_version_counts to a work-weighted tally over the same [9/10*CL, CL] window at the same 60% threshold the accept gate enforces, so activation strictly implies the accept rule and a minted boundary share can never be rejected. BTC tracker had only get_desired_version_counts, so this also ports get_desired_version_weights onto btc/share_tracker.hpp (work-weighted via get_index()->work, mirroring the ltc/dgb tracker shape). Per-coin lane, btc/ tree only; v36-native consensus-version-signaling standardization. Merge held pending BTC crossing re-soak. * btc(#290): replace 95%-flat should_punish accept gate with canonical 60%-work switch rule Completes the mint<->accept coupling. The prior commit (27a4463) made the AutoRatchet MINT guard work-weighted, but BTC share_check step 2 still admitted a version BOUNDARY via the non-canonical 95%-flat-COUNT should_punish_version path -- the inverse of the #97 LTC wedge: a work-weighted mint guard in front of a flat-count accept gate. Port the canonical accept rule from ltc/share_check.hpp ~1715-1762 (p2pool data.py check() 1396-1414): a boundary share (share.version != parent.version) is valid only when, in the window [CHAIN_LENGTH*9/10, CHAIN_LENGTH] behind the PARENT, the new version holds >= 60% of the PPLNS-WORK-WEIGHTED desired-version tally (get_desired_version_weights). same-version always valid; -1 downgrade valid (AutoRatchet V35-after-V36); any other jump throws "invalid version jump". Removes the should_punish_version call from the accept path entirely (F10/(b): the 60% switch rule is now the ONLY version gate, matching ltc/dgb). Per-coin lane, src/impl/btc/ only. Semantics byte-mirror LTC. Merge held pending full gh CI rollup CLEAN + BTC crossing re-soak; no self-merge. --------- Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
Mirror of LTC v36 commit 18dd945 onto the BTC variant. All three gentx sites (generate_share_transaction / create_local_share_v35 / create_local_share) now exclude BOTH donation scripts (COMBINED + P2PK) from per-miner payout dests and fold the COMBINED_DONATION_SCRIPT-keyed weight into the single donation-last output. Rides core/donation.hpp SSOT (FLAG6 #139). No F1 entanglement (F11 touches only the payout-sort path). Prep-branch staging; PR stays unopened until integrator dispatches the F-mirror post crossing-soak (#97). +51/-6, matches LTC F11 line counts.
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
…te (contract) Mirrors the intent of ltc 865fdd7 onto the DGB lane. ltc-doge root-caused the v35->v36 crossing wedge (#97): the AutoRatchet mint gate activated on a 95%-by-flat-COUNT desired-version tally, but the consensus accept gate (share_check step 2 / p2pool check() data.py:1399) admits a V36 boundary share only at the WORK-WEIGHTED 60% rule (canonical get_desired_version_counts, data.py:2651, weights each share by target_to_average_attempts). Under heterogeneous hashrate the flat count outran the work-weighted accept gate, so the minter produced boundary shares every peer rejected -> frozen crossing. DGB carries NO AutoRatchet minter yet (no src/impl/dgb/auto_ratchet.hpp; the minter lands in Phase B). The DGB accept gate (share_check.hpp version-switch rule) is ALREADY work-weighted/canonical via get_desired_version_weights, and that accessor is KAT-pinned (#249). The ONLY artifact of the unsafe F10 flat-count choice on this lane is the design-contract recorded in comments, which would seed the exact wedge into the future port. This corrects those comments so DGB's AutoRatchet is born work-weighted: its activation tail guard MUST read get_desired_version_weights over the same [9/10*CL, CL] window at the same 60% threshold the accept gate enforces, superseding F10. v36-native shared structure (consensus version-signaling), standardized cross-coin for a clean v37 migration. Comment-only; zero behavior change (no minter exists to alter). Per-coin isolation preserved: touches src/impl/dgb/ only.
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
The v35->v36 crossing soak (#97) wedged: AutoRatchet activated on a 95%-by-COUNT desired-version tally, but the consensus accept gate (share_check step 2 / p2pool check() data.py:1399) admits a V36 boundary share only when desired-version reaches 60% by WORK over the [9/10*CL, CL] window -- canonical get_desired_version_counts (data.py:2651) weights each share by target_to_average_attempts(target). Under heterogeneous hashrate a small miner set can carry the COUNT past 95% while the work-weighted tail sits below 60%, so the activated node mints a V36 boundary share that every peer rejects, freezing the crossing. Switch AutoRatchet tail guard from flat-count get_desired_version_counts to work-weighted get_desired_version_weights, over the same window at the same 60% threshold the accept gate enforces. Activation now strictly implies the accept rule, so a minted boundary share can never be rejected. Supersedes the earlier F10 keep-activation-flat-count choice, which the soak proved unsafe. Covers LTC and DOGE (merged, rides LTC AutoRatchet).
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
…pling ltc: couple AutoRatchet mint gate to the work-weighted accept gate (fix #97 crossing wedge)
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
) * btc: couple AutoRatchet mint gate to the work-weighted accept gate Mirror of the LTC mint<->accept coupling fix onto the BTC lane. The v35->v36 crossing soak (#97) wedged on LTC: AutoRatchet activated on a 95%-by-flat-COUNT desired-version tally, but the consensus accept gate (share_check step 2 / p2pool check() data.py:1399) admits a V36 boundary share only when desired-version reaches 60% by WORK over the [9/10*CL, CL] window -- canonical get_desired_version_counts (data.py:2651) weights each share by target_to_average_attempts(target). Under heterogeneous hashrate a small miner set can carry the COUNT past 95% while the work-weighted tail sits below 60%, so the activated node mints a V36 boundary share that every peer rejects, freezing the crossing. btc/auto_ratchet.hpp carried the byte-identical flat-count tail guard, so the same latent wedge existed on this SHA256d sharechain. Switch the tail guard from get_desired_version_counts to a work-weighted tally over the same [9/10*CL, CL] window at the same 60% threshold the accept gate enforces, so activation strictly implies the accept rule and a minted boundary share can never be rejected. BTC tracker had only get_desired_version_counts, so this also ports get_desired_version_weights onto btc/share_tracker.hpp (work-weighted via get_index()->work, mirroring the ltc/dgb tracker shape). Per-coin lane, btc/ tree only; v36-native consensus-version-signaling standardization. Merge held pending BTC crossing re-soak. * btc(#290): replace 95%-flat should_punish accept gate with canonical 60%-work switch rule Completes the mint<->accept coupling. The prior commit (27a4463) made the AutoRatchet MINT guard work-weighted, but BTC share_check step 2 still admitted a version BOUNDARY via the non-canonical 95%-flat-COUNT should_punish_version path -- the inverse of the #97 LTC wedge: a work-weighted mint guard in front of a flat-count accept gate. Port the canonical accept rule from ltc/share_check.hpp ~1715-1762 (p2pool data.py check() 1396-1414): a boundary share (share.version != parent.version) is valid only when, in the window [CHAIN_LENGTH*9/10, CHAIN_LENGTH] behind the PARENT, the new version holds >= 60% of the PPLNS-WORK-WEIGHTED desired-version tally (get_desired_version_weights). same-version always valid; -1 downgrade valid (AutoRatchet V35-after-V36); any other jump throws "invalid version jump". Removes the should_punish_version call from the accept path entirely (F10/(b): the 60% switch rule is now the ONLY version gate, matching ltc/dgb). Per-coin lane, src/impl/btc/ only. Semantics byte-mirror LTC. Merge held pending full gh CI rollup CLEAN + BTC crossing re-soak; no self-merge. --------- Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
frstrtr
added a commit
that referenced
this pull request
Jun 23, 2026
Mirror of LTC v36 commit 18dd945 onto the BTC variant. All three gentx sites (generate_share_transaction / create_local_share_v35 / create_local_share) now exclude BOTH donation scripts (COMBINED + P2PK) from per-miner payout dests and fold the COMBINED_DONATION_SCRIPT-keyed weight into the single donation-last output. Rides core/donation.hpp SSOT (FLAG6 #139). No F1 entanglement (F11 touches only the payout-sort path). Prep-branch staging; PR stays unopened until integrator dispatches the F-mirror post crossing-soak (#97). +51/-6, matches LTC F11 line counts.
This was referenced Jul 17, 2026
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.