Skip to content

dash(stratum): serve real work — wire GBT work data into session template path + X11 submit scoring#733

Merged
frstrtr merged 3 commits into
masterfrom
fix/dash-stratum-work-serving
Jul 17, 2026
Merged

dash(stratum): serve real work — wire GBT work data into session template path + X11 submit scoring#733
frstrtr merged 3 commits into
masterfrom
fix/dash-stratum-work-serving

Conversation

@frstrtr

@frstrtr frstrtr commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Fixes #732 — the v0.2.2 DASH stratum accepts and registers miners but never serves work: DASHWorkSource stages 4c/4d were unimplemented skeletons, so StratumSession::send_notify_work() saw an empty template forever and re-armed its 1 s timer in a loop while the binary logged a misleading [LTC] wait line.

Stage 4c — template serving

Bridges the armed get_work() seam (#726: embedded coin-state when seeded, retained dashd GBT fallback otherwise — the duality is preserved untouched) into the template trio the coin-agnostic session consumes:

  • Template cache (cached_work()): the DashWorkData snapshot is cached under template_mutex_, keyed on work_generation_ + a 30 s staleness TTL, with a 5 s negative cache so a down dashd is not hammered by every session's 1 s retry timer. A refresh that observes a moved tip bumps work_generation_ so sessions re-push work between timer firings.
  • get_current_work_template(): GBT-shaped JSON with exactly the fields send_notify_work() reads (previousblockhash BE display hex, version, bits as 8-char BE hex, curtime, height, coinbasevalue), mirroring the LTC/BTC conventions. Empty object on an honest set-gap — never fabricated.
  • get_stratum_merkle_branches(): sibling list over [coinbase placeholder, tx1..txN] in the LE-internal-bytes wire encoding (the existing dash::coinbase::merkle_branches_raw/_hex SSOT).
  • build_connection_coinbase(): goes through the EXISTING verifier-shared SSOTs — compute_dash_payouts (worker_tx ‖ masternode/superblock payments ‖ donation tail, the share_check.hpp gentx order) → coinbase::build (BIP34 height + pool tag scriptSig, DIP3/DIP4 payload, ref_hash + nonce64 OP_RETURN tail) → split_coinb (coinb1/coinb2 around the 8-byte nonce64 extranonce slot = en1(4) ‖ en2(4)). No second payout or serialization implementation exists by construction, so the coinbase a miner hashes is byte-identical to the verifier split.
    • Genesis case (fresh pool, empty sharechain — the deployment reality): the single connecting miner's P2PKH script carries the full worker payout (weights {script:1}, total 1) + the GBT-mandated masternode outputs + donation tail. The PPLNS multi-output path rides the new set_pplns_weights_fn seam (ShareTracker walk bound by the run-loop) through the same compute_dash_payouts arm.
    • get_coinbase_parts() serves an all-to-donation pool fallback so the core's legacy hardcoded (LTC-shaped) default coinbase is unreachable for DASH.
  • get_current_gbt_prevhash(): same cached snapshot as the template — one truthful source.
  • set_difficulty scale fix: the ctor overrides set_difficulty_multiplier to 1.0 (X11 uses the standard diff-1 scale, p2pool-dash DUMB_SCRYPT_DIFF = 1), same as the SHA256d BTC work source. The default 65536 (scrypt convention) inflated the advertised difficulty 65536× (the wire=32.768 in the field report).

Stage 4d — submit scoring

  • mining_submit(): reconstructs coinbase = coinb1 ‖ en1 ‖ en2 ‖ coinb2, sha256d txid, ascends the frozen branches, serializes the 80-byte header via the block_producer.hpp SSOT, runs the --selftest-pinned dash::crypto::hash_x11, and classifies tighten-first:
    • WonBlock (pow ≤ block target): full block assembled with the exact --mine-block serialization (header ‖ CompactSize(1+ntx) ‖ coinbase ‖ txs; DASH has no segwit) → submit_block_fn_, the dual-path won-block dispatcher bound in main_dash.cpp (dashd submitblock RPC arm; embedded P2P relay leg folds into that same closure when it lands). A won block reaching no sink logs at ERROR — never a silent drop.
    • ShareAccept (pow ≤ share target): routed into the new set_mint_share_fn seam (header/coinbase/branches/payout script/pow). While unbound it accepts for vardiff and logs loudly.
    • else low-difficulty reject (code 23); missing JobSnapshot rejects 21.
  • compute_share_difficulty(): same reconstruction ending in diff1 / x11(header), so the coin-agnostic vardiff gate engages; 0.0 on malformed input (the documented sentinel).

Secondary fix — [LTC] mistag in the coin-agnostic core

stratum_server.cpp hardcoded [LTC] in the "Waiting for block template" warning. StratumConfig now carries a coin_symbol set at runtime (DASH/BTC/DGB set it in their work-source ctors, LTC in main_ltc.cpp), with a neutral [Stratum] fallback. No coin name is hardcoded in src/core.

Test gate (the miss that let v0.2.2 ship)

Extended the existing allowlisted test_dash_stratum_work_source target (already in the build.yml --target list and ctest registration — the #729 compile-into-existing-target pattern, no workflow-file change needed):

  • stub work source returning a fixed DashWorkData → non-empty template with correct prevhash/bits/version/curtime encodings; set-gap fixture → honestly empty trio;
  • merkle branches fold to the canonical compute_merkle_root over the fixture tx set;
  • coinb1 ‖ extranonce ‖ coinb2 reassembles byte-identical to the compute_dash_payouts + coinbase::build SSOT output, with value splits pinned (masternode output = exact GBT amount, miner + donation = worker payout, total = coinbasevalue);
  • compute_share_difficulty KAT: fixture header → pinned X11-derived value; malformed input → 0.0;
  • mining_submit classification: deterministic (test-mined nonce) ShareAccept / low-diff reject / WonBlock, the WonBlock asserting the dispatched block bytes are the exact --mine-block serialization; plus the mint-seam routing test.

Verified

  • test_dash_stratum_work_source — 19/19 KATs green locally (Release/gcc-13, conan profile ci/conan/linux-gcc13.profile); test_stratum_extensions (44), test_dash_stratum_extranonce_split (6), test_dash_stratum_submit_reassembly (7) all green.
  • c2pool-dash --selftest green (X11 KATs + coinbase parity untouched).
  • Live smoke (--run --stratum against a stubbed dashd GBT endpoint): subscribe → authorize → mining.notify arrives with a real job — BIP34 height push + c2pool tag + PPLNS/masternode/donation/OP_RETURN outputs, correct stratum prevhash chunking, set_difficulty wire value now 0.0005 (was 32.768). The exact dash: stratum serves no work — DASHWorkSource 4c/4d skeleton (v0.2.2 field report); [LTC] mistag in core #732 field failure mode (miner registered, no job, 60 s EOF loop) is gone. With no dashd reachable, the honest set-gap path logs [DASH] Waiting for block template ... — the coin-tag fix observed live.
  • c2pool (LTC), btc_stratum, dgb_stratum targets rebuilt green (core StratumConfig change is additive; no positional aggregate inits exist).

Explicitly deferred (follow-ups, called out in-code)

  • Sharechain mint wiring: main_dash.cpp does not yet stand up a DASH ShareTracker in the --run loop, so the node-side share-creation seam is not cleanly reachable; set_mint_share_fn is the one-line binding point and unbound share-target submissions accept-for-vardiff with a loud log.
  • PPLNS multi-output weights: set_pplns_weights_fn is the ShareTracker-walk binding point; until bound the genesis single-miner split is served (correct for a fresh pool's empty sharechain).
  • Embedded P2P won-block relay leg remains S8 (unchanged; the RPC arm is live).

frstrtr and others added 3 commits July 18, 2026 00:07
…wait tag

The coin-agnostic core hardcoded "[LTC]" in the send_notify_work
no-template warning, so a DASH binary logged LTC and sent the operator
diagnosing the wrong coin (#732 secondary defect). StratumConfig now
carries a runtime coin_symbol -- LTC sets it in main_ltc.cpp, BTC/DGB in
their work-source ctors (DASH follows in the 4c/4d commit) -- with a
neutral [Stratum] fallback when unset. No coin name remains hardcoded in
src/core.
Stage 4c (template serving): bridge the armed get_work() seam (#726,
embedded coin-state when seeded / retained dashd GBT fallback -- duality
preserved) into the template trio the coin-agnostic session consumes.
DashWorkData is cached under template_mutex_ keyed on work_generation_ +
a 30s staleness TTL (5s negative cache; tip-move bumps the generation).
get_current_work_template() emits exactly the fields send_notify_work()
reads with LTC/BTC byte conventions; merkle branches ride the existing
merkle_branches_raw/_hex SSOT; build_connection_coinbase() goes through
the verifier-shared compute_dash_payouts -> coinbase::build ->
split_coinb SSOTs (coinb1/coinb2 around the 8-byte nonce64 extranonce
slot), so the coinbase a miner hashes is byte-identical to the verifier
split by construction. Genesis case (fresh pool, empty sharechain): the
connecting miner's P2PKH script carries the full worker payout + the
GBT-mandated masternode/superblock outputs + donation tail; the PPLNS
multi-output path rides the new set_pplns_weights_fn seam through the
same SSOT arm. Ctor pins set_difficulty_multiplier to 1.0 (X11 =
standard diff-1 scale) -- the scrypt 65536 default inflated the
advertised difficulty 65536x (the wire=32.768 in the field report).

Stage 4d (submit scoring): mining_submit() reassembles the coinbase,
folds the frozen branches, serializes the 80-byte header via the
block_producer SSOT, runs the selftest-pinned X11, and classifies
tighten-first: WonBlock -> full block in the exact --mine-block
serialization -> submit_block_fn_ dual-path dispatcher (a won block
reaching no sink screams, never drops); ShareAccept -> new
set_mint_share_fn seam (accept-for-vardiff + loud log while unbound);
else low-difficulty reject. compute_share_difficulty() is the same
reconstruction ending in diff1/x11(header) so vardiff engages.

Test gate (the miss that let v0.2.2 ship): test_dash_stratum_work_source
(already in the build.yml allowlist + ctest registration) now carries
the 4c/4d KATs -- fixed-DashWorkData template trio with pinned
encodings, set-gap honesty, branch fold vs compute_merkle_root, coinbase
reassembly byte-identical to the SSOT build with pinned value splits,
X11 difficulty KAT, and deterministic mining_submit classification
(share accept / low-diff reject / won-block bytes / mint-seam routing).

Verified: 19/19 KATs green; c2pool-dash --selftest green; live smoke
(--run --stratum vs a stubbed GBT endpoint) serves mining.notify with a
real job on subscribe -- the #732 field failure mode is gone.
…oke)

On Windows raise_nofile_limit() always returns 0 (no RLIMIT_NOFILE). The
prior LOG_WARNING sent this benign startup notice to stderr; the CI --help
smoke pipes c2pool.exe through pwsh "2>&1 | Select-Object -First N", whose
early pipe-close promotes any native stderr write to a NativeCommandError
and exits 1. Emit the notice on stdout under _WIN32 (matching main_dash.cpp);
POSIX keeps LOG_WARNING for genuine getrlimit failures. SAFE-COSMETIC.
@frstrtr
frstrtr merged commit 429afb4 into master Jul 17, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dash: stratum serves no work — DASHWorkSource 4c/4d skeleton (v0.2.2 field report); [LTC] mistag in core

1 participant