You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
dash: a busy tracker must not forfeit the block-winning share (#889) (#903)
add_local_share acquires the exclusive tracker with std::try_to_lock and
DECLINES when the compute thread is mid-think(). For an ordinary share that is
a sound trade -- the decline costs one share and the NEXT solve mints instead.
A block-winning share has no next solve: the block is found once, so the same
decline permanently forfeits the highest-work share the node will ever produce.
And it is not only share weight. p2pool nodes do not learn about a pool block
from a block announcement; they detect it THROUGH THE SHARECHAIN, by watching
for a share with pow_hash <= header['bits'].target (p2pool/node.py:145-147).
A block-winning share that never enters the sharechain is a won block that NO
peer -- including our own oracle -- can ever record. A try_to_lock decline
therefore reproduces the FULL #887 symptom (invisible block AND lost weight),
just intermittently and far harder to notice. This is the residual hole PR #888
left open and it must ship with it.
THE FIX. The mint path carries a won_block flag from the stratum classify arm
down to the two tracker acquisitions on it, and takes a BOUNDED WAIT on that
path ONLY. The ordinary share path is byte-for-byte unchanged: Urgency::
Opportunistic is literally the same single try_to_lock, and it is the default
argument everywhere, so nothing can inherit the wait by omission.
BOUND: 2000 ms, chosen from measurement, not taste. Over a 42 h DASH sharechain
soak ([ASYNC-THINK] compute done in Nms, n=6721 post-join think cycles):
p50 125 ms, p90 193 ms, p99 300 ms, p99.9 512 ms, max 1074 ms; >500 ms in 8
cycles (0.119%), >1000 ms in exactly 1 (0.015%). 2000 ms covers 100% of the
observed steady-state distribution with ~1.9x headroom over the single worst
cycle, while capping well below the 6-15 s bootstrap think cycles the same log
shows in the first ~5 min after a cold join (a node in bootstrap has no
sharechain to mint onto and is not winning blocks). An expired budget is a
LOG_ERROR plus a forfeit counter, never a silent drop.
Polled try_lock rather than a blocking lock(): it must be bounded, and
std::shared_mutex has no timed acquire -- switching to shared_timed_mutex would
rewrite ~30 reader-discipline call sites across node.hpp/node.cpp for no gain.
The poll keeps the mutex type and every existing call site identical.
BLOCK SUBMIT ORDERING IS UNCHANGED AND UNCONDITIONAL. The won-block arm
dispatches the block via submit_block_fn_ and only THEN invokes the mint seam
(#887/#888). Whatever the wait costs, the block is already out; an ordering
witness in the new end-to-end KAT asserts submit_called was already true when
the mint ran, so a future reorder goes red.
CALLER-SIDE LOCK TRACE (#878/#881), re-verified against this tree, not taken on
trust from #888: asio handler -> StratumSession::process_message -> handle_submit
-> mining_submit carries no mutex at all (grep for mutex/lock over
stratum_server.cpp:1030-1360 is empty); mining_submit copies mint_share_fn_
under a scoped mint_share_mutex_ and releases before calling it, and
found_block_mutex_ on the block arm is likewise scoped and released first; the
mint lambda's read guard is scoped and RELEASED before add_local_share takes the
exclusive lock -- never nested. Zero locks held on entry, which is what makes a
bounded wait viable rather than a deadlock. is_compute_thread() is the
belt-and-braces guard: the compute thread already owns the lock and never waits.
NO CONSENSUS CHANGE. Share construction, target derivation and serialisation are
untouched; this changes only how hard we try to acquire a lock.
Tests folded into EXISTING allowlisted targets (no new add_executable):
test_dash_node 13/13 PASSED (+5, +dash/sharechain link so the
KATs drive the REAL add_local_share against the
REAL m_tracker_mutex held by another thread)
test_dash_stratum_work_source 50/50 PASSED (+4, incl. an end-to-end
block-target submit through an exclusively
held tracker)
All 9 verified as REGISTERED AND EXECUTED under ctest (#895), with non-zero
durations. Negative control (block path forced back to Opportunistic +
won_block forced false) reddens 4 of them and leaves the ordinary-path controls
green, as designed.
SCOPE: dash only. btc/bch reach the same seam through CreateShareFn, whose
signature carries no solve class; widening it touches four more files plus three
test binders and is not the hotel-deploy path. dgb cannot mint any local share
until #884. Tracked as follow-ups, not silently assumed fixed.
0 commit comments