dgb(#82): bind 5 won-block reconstruct seams to live tracker (decision (a), deadlock-safe) - #267
Merged
frstrtr merged 1 commit intoJun 20, 2026
Conversation
…n a, deadlock-safe) Replace the interim nullopt reconstruct stub in main_dgb.cpp --run path with the faithful make_reconstruct_closure (#265), bound to the LIVE ShareTracker via 5 seams: share_fields / gentx_bytes / nth_parent / new_tx_hashes / known_txs. Adds a NON-LOCKING const-ref known_txs() accessor to dgb::NodeImpl (node.hpp). THREADING -- decision (a) (integrator + decisions, 2026-06-20): the prior approval assumed m_on_block_found fires lock-free on the IO thread and the seams would take a shared_lock snapshot. That premise was FALSIFIED: the callback fires on the COMPUTE thread, which ALREADY holds m_tracker_mutex EXCLUSIVELY (run_think node.cpp:1433 unique_lock -> think -> attempt_verify -> share_tracker.hpp:537). A shared_lock from the lock-owning thread on the non-recursive std::shared_mutex is UB/self-deadlock. Fix (a): drop the by-value snapshot_known_txs(), read chain + known-tx state DIRECTLY under the exclusive lock the compute thread already owns (non-locking accessor). HARD CONDITION verified (integrator): none of the 5 seam reads re-takes m_tracker_mutex -- grep confirms the mutex lives ONLY in NodeImpl (node.cpp/node.hpp); ShareTracker + ShareChain (chain.get_share, get_nth_parent_via_skip) and generate_share_transaction contain ZERO mutex/lock_guard/unique_lock/shared_lock. So (a) does NOT reintroduce the recursive self-deadlock one frame deeper. SHARE-VERSION GUARD: m_tx_info (new_transaction_hashes + transaction_hash_refs) is on-wire ONLY for share version < 34 (Share=17 / NewShare=33 -- the p2pool-dgb-scrypt oracle baseline). The generic ShareVariants::invoke must compile for all 5 variants, incl. v34+ segwit/merged which carry no tx refs in the DGB share format, so the two m_tx_info seams are if-constexpr requires-guarded and fail closed (throw -> caught -> nullopt, never a malformed broadcast) for any unexpected-for-DGB v34+ won share. Fenced to main_dgb.cpp + node.hpp. build_dgb green; #265 KAT 6/6 green; binary --help/selftest OK. The #82 dual-path broadcaster gate stays OPEN: the submitblock-RPC arm is still unarmed (coin_node rpc=nullptr, #163 seam guard = deferred-not-dropped, loud) and neither arm is verified until the live won-block-reaches-network test. HOLD merge (stacked on #265).
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.
#82 run-loop consume — bind the 5 reconstruct seams to the LIVE tracker (decision (a))
Stacked on #265 (base branch
dgb/won-block-reconstruct-closure). Fenced tomain_dgb.cpp+node.hpponly — no shared/bitcoin_family/cross-coin touch.What
Replaces the interim
return nulloptreconstruct stub inrun_node()with the faithfulmake_reconstruct_closure(#265), bound to the liveShareTrackervia 5 seams:share_fields→chain.get_share(h)→ {min_header, merkle_link, tx refs}gentx_bytes→generate_share_transaction(...)SSOT regennth_parent→chain.get_nth_parent_via_skipnew_tx_hashes→ sharem_new_transaction_hashesknown_txs→ new non-lockingNodeImpl::known_txs()const-ref accessorDecision (a) — deadlock-safe (integrator + decisions, 2026-06-20)
The earlier-approved shared_lock snapshot would self-deadlock:
m_on_block_foundfires on the COMPUTE thread, which already holdsm_tracker_mutexexclusively (run_thinknode.cpp:1433unique_lock→ think → attempt_verify → share_tracker.hpp:537). Ashared_lockfrom the lock-owning thread on the non-recursivestd::shared_mutexis UB. Fix (a): droppedsnapshot_known_txs(), read state directly under the held exclusive lock via the non-locking accessor.HARD CONDITION verified: none of the 5 seam reads re-takes
m_tracker_mutex.grepconfirms the mutex lives only inNodeImpl;ShareTracker+ShareChain+generate_share_transactioncontain zero mutex/lock usage. (a) does not reintroduce the recursive deadlock a frame deeper.Share-version guard
m_tx_info(tx refs) is on-wire only for share version < 34 (Share=17 / NewShare=33 — the p2pool-dgb-scrypt oracle baseline). The genericShareVariants::invokemust compile for all 5 variants, so the twom_tx_infoseams areif constexpr (requires {...})guarded and fail closed for any unexpected-for-DGB v34+ segwit/merged won share.Evidence
build_dgbgreen (only pre-existing warnings); binary--help/selftest OKGate status
The #82 dual-path broadcaster gate stays OPEN. The submitblock-RPC arm is still unarmed (
coin_noderpc=nullptr; #163 seam guard = deferred-not-dropped, loud). Neither arm is verified until the live won-block-reaches-network test. HOLD merge (lands after #265).