dash(share-producer): prospective share_info assembly, ref_hash/hash_link producers, retarget + KATs (mint campaign 1/3)#734
Merged
Conversation
…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.
…the field value verbatim Oracle data.py:277-289: the outer contents['coinbase_payload'] VALUE is pack.VarStrType().pack(raw_payload) — it already carries one compactsize prefix — and Share.__init__ (data.py:346-348) appends that value VERBATIM to the check_hash_link data. On the wire the PossiblyNone(b'', VarStr) layer adds one more prefix, so DashFormatter's single strip leaves m_data == [compactsize][raw] for a live oracle share. The previous fix (#412) correctly established that the compactsize belongs in the hash_link data, but implemented it by re-VarStr-ing m_data — a SECOND prefix on top of the one m_data already carries — diverging gentx_hash from the oracle on every real DIP4 CbTx share (every modern mainnet-template share, in both accept directions). Append m_data verbatim; preserve the empty branch (oracle appends b'', not 0x00). Update the end-to-end anchor in test_dash_share_hash_link to discriminate all three framings (verbatim / double-prefix / bare-raw) and document the field-value convention on DashShare.
…link producers, oracle retarget Producer-side share construction for the pre-v36 v16 lineage (mint campaign slice 1/3), hand-traced from the p2pool-dash oracle (p2pool/data.py generate_transaction/get_share/get_ref_hash/ prefix_to_hash_link, p2pool/dash/data.py FloatingInteger + tx_type, p2pool/networks/dash.py): - generate_prospective_share_info: share_data carry-through, tx forwarding refs over the past min(height,100) shares (first-seen dedup, [share_count, tx_count] pairs), far_share_hash 99-ancestor walk, timestamp clip (prev+1 .. prev+2*SP-1), absheight/abswork accumulation mod 2^32 / 2^128. - compute_share_target[_pure]: the ORACLE retarget only — APS on the min_work basis, 2^256//(SP*aps)-1, +-10% clamp on prev max_target, [0, MAX_TARGET] clamp, truncating compact. Deliberately NOT the tracker's compute_share_target, which carries a v36 emergency decay absent from the dash oracle. - get_cumulative_weights: the oracle PPLNS window — starts at the GRANDPARENT (prev.prev, data.py:181) with max(0,min(height,RCL)-1) shares and the oracle's exact partial-inclusion division order. - serialize_share_info + compute_ref_hash: v16 ref preimage, byte-equal to the verifier recompute. - build_gentx: full oracle coinbase bytes (int16 version+type, worker || payments || donation || OP_RETURN(ref_hash||nonce64), VarStr extra_payload) + the hash_link prefix cut; worker_payout subtracts only payments actually emitted (oracle rule). - prefix_to_hash_link: SHA256 midstate producer, inverse of check_hash_link, with the oracle endswith(const_ending) assertion. - build_share: full DashShare assembly with a MANDATORY self-verify — the verifier hash_link fold must reproduce the produced gentx txid and the share must pass share_init_verify before it could reach the wire. Nothing is wired into --run or stratum; slices 2-3 bind this to the node legs and DASHWorkSource::set_mint_share_fn.
…rget pins, wire round-trip
25 tests in the existing allowlisted test_dash_share_hash_link target
(CI build.yml allowlist untouched):
(a) ref stream/ref_hash goldens (179-byte F1 preimage pinned hex) and
built shares passing share_init_verify / verify_share;
(b) wire round-trip byte-identical through DashFormatter + RawShare +
load_share, with the oracle outer-payload double-prefix framing
pinned on the wire tail;
(c) retarget KATs at five chain positions (genesis, short chain,
deep-uniform MAX clamp, fast-pool 9/10 clamp 0x1c1ccccb, slow-pool
11/10 clamp 0x1c233332) — exact big-int traces of the oracle
formula, truncating-compact discriminators;
(d) absheight/abswork accumulation incl. wrap at 2^32 / 2^128;
(e) hash_link producer->checker round trip swept across SHA256 block
boundaries, plus a drift fence pinning the producer merkle walk to
coinbase::merkle_branches_raw.
Goldens derived by hand-tracing the oracle source with exact big-int
arithmetic; PROVISIONAL values are marked in-file and listed in the PR
body for oracle-replay confirmation.
…-clip saturation, self-verify scope note - pool_attempts_per_second: enforce height >= dist at the helper boundary so no future caller can receive a silently-wrong span from the skip list's exhaust fallback (the oracle only reaches this with the TARGET_LOOKBEHIND gate already satisfied). - clip_timestamp: compute the clip window in 64-bit and saturate at 2^32-1 — the oracle uses unbounded ints, so a wrapped uint32 window would be a silent divergence on a bogus far-future prev timestamp. - build_share: document the exact scope of the mandatory self-verify (serialization self-consistency; amount-level oracle parity is carried by the KAT layer + oracle replay of the PROVISIONAL goldens).
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.
This PR is NOT header-only / SAFE-ADDITIVE. Actual diff: +2932/-175 across 16 files, two payloads:
src/impl/dash/{share,share_check,share_producer}.hpp+impl/dash/stratum/*+ 3 KATs. Wires nothing into--run(that is slices 2-3).coin_symboltosrc/core/stratum_types.hpp, uses it insrc/core/stratum_server.cpp(replaces hardcoded "[LTC]" with runtime tag, neutral "[Stratum]" fallback), and sets it per-coin inmain_ltc.cpp+impl/btc+impl/dgb+impl/dashwork sources. Zero wire-byte change; all six coin smokes green.The #732 fix was folded in to give the DASH stratum path correct self-tagging; it is intentional, not a shared-base ride-along. It crosses
src/core+ 3 coin trees, so per-coin-isolation review applies — it clears only because it is uniform/mechanical/wire-neutral. If the operator prefers a clean split, I will extract #732 to its own PR and rebase this one; otherwise it ships as reviewed.Scope — mint campaign slice 1/3 (producer-side share construction + oracle goldens)
c2pool-dash's stratum serves work (#733) but cannot MINT shares onto the live pre-v36 p2pool-dash sharechain — no producer-side share machinery existed in
src/impl/dash/. This slice builds it, KAT-gated, wiring NOTHING into--runor stratum (that is slices 2-3: node legs +DASHWorkSource::set_mint_share_fnbinding).New:
src/impl/dash/share_producer.hpp(dash::producer, header-only, mirrors the DGB mint-campaign layering —run_loop_mint.hpp/work_ref_hash.hpppure-core + thin chain wrappers):generate_prospective_share_info(chain, params, ProducerJobInputs): prev hash / share nonce / subsidy / donation / packed_payments carry-through,assemble_tx_refs(tx forwarding over the pastmin(height,100)shares, first-seen dedup,[share_count, tx_count]pairs — data.py:147-170),compute_far_share_hash(99-ancestor walk, data.py:235), oracle retargetcompute_share_target[_pure](data.py:135-145: APS on the min_work basis →2^256//(SP·aps)-1→ ±10% clamp on prevmax_target→[0, MAX_TARGET]→ truncating compact), timestamp clip (prev+1 .. prev+2·SP-1), absheight/abswork accumulation mod2^32/2^128.serialize_share_info(exact v16 ref preimage layout) +compute_ref_hash(identifier ‖ share_info → sha256d → ref merkle link). Byte-equal to the verifier recompute inshare_check.hpp(golden-pinned).prefix_to_hash_link(data.py:34-37): SHA256 midstate over the gentx prefix (packed_gentx[:-payload_varstr-32-8-4]), inverse ofcheck_hash_link, with the oracle'sendswith(const_ending)assertion.build_share: oracle cumulative-weights window → full gentx byte assembly (build_gentx, oracle dashtx_type: int16 version+type, coinbase input, worker‖payments‖donation‖OP_RETURN(ref_hash‖nonce64) outputs, VarStr extra_payload) → hash_link → merkle_link (calculate_merkle_link_index0, drift-fenced againstcoinbase::merkle_branches_raw) → non-optional self-check: (a) verifiercheck_hash_linkfold must reproduce the produced gentx txid, (b) fullshare_init_verifypass; any mismatch throws before a share could ever reach the wire.test/test_dash_share_producer.cpp(25 tests in the target, all green), compiled into the EXISTING allowlistedtest_dash_share_hash_linktarget (CIbuild.ymlallowlist untouched — token workflow-scope pattern):share_init_verify/verify_share;RawShare+load_share), outer-payload framing pinned;9/10clamp0x1c1ccccb, slow-pool→11/10clamp0x1c233332; truncating-compact discriminators);2^32/2^128;One consensus-bearing verifier fix (review closely)
share_check.hpp::share_init_verifyoutercoinbase_payloadframing. Oracle (data.py:277-289): the outercontents['coinbase_payload']VALUE ispack.VarStrType().pack(raw_payload)— i.e. the field value already carries ONE compactsize prefix — andShare.__init__(data.py:346-348) appends that value VERBATIM to thecheck_hash_linkdata. On the wire thePossiblyNone(b'', VarStr)layer adds one MORE prefix, soDashFormatter's single strip leavesm_data == [compactsize][raw]for a live oracle share. #412 correctly put the compactsize into the hash_link data, but implemented it by re-VarStr-ingm_data— a SECOND prefix on top of the onem_dataalready carries, diverginggentx_hashfrom the oracle on every real DIP4 CbTx share (i.e. every modern mainnet-template share, in BOTH directions: accepting live shares and having ours accepted). Fixed to a verbatim append; empty-branch (b"", no 0x00) preserved;test_dash_share_hash_link.cpptest 2 updated to discriminate all three framings (verbatim/double-prefix/bare-raw). The producer fillsm_data = VarStr(raw)accordingly and the F2 KAT pins the wire tail00 05 04 de ad be ef(double prefix on wire).Oracle-tracing method
Goldens were derived by hand-tracing github.com/frstrtr/p2pool-dash master (
p2pool/data.py,p2pool/dash/data.py,p2pool/util/pack.py,p2pool/networks/dash.py) with exact big-integer arithmetic (CPython ints/hashlib for digests) — the oracle PROCESS was not run. Byte layouts were hand-assembled from the pack-type definitions and pinned as hex; retarget/weights constants are exact integer traces of the formulas.PROVISIONAL goldens (need oracle-replay confirmation)
m_datapost-strip is[compactsize][raw]. The in-repo tests exercise the full serialize→parse→verify pipeline but the wire layout itself is a source trace.<99 → Noneand≥100 → nth(prev,99)are unambiguous).0x1c1ccccb/0x1c00f5c2/0x1c233332/0x1c088880(exact traces ofFloatingInteger.from_target_upper_bound+math.clip; mantissa-truncation matches the in-treetarget_to_bits_upper_bound).0x733373330000,0xccc8ccc8000) — exact trace ofWeightsSkipList.apply_deltadivision order.Ban-risk hotspots (hardest-to-pin semantics — review focus)
previous_share.previous_share_hash(the GRANDPARENT, data.py:181) withmax(0, min(height, RCL)-1)shares; the in-treegenerate_share_transactionwalks fromprevitself — off by one vs the oracle (invisible on homogeneous chains, wrong payouts on heterogeneous ones). Producer implements the oracle window (KATOracleWindowStartsAtGrandparent). Traced consumers:generate_share_transactioncurrently has ZERO dash callers (the accept path isshare_init_verify+ the version-transition gate), so nothing in-tree rejects the producer's shares over this — but that reconstructor should be conformed to the oracle (window start, unconditional-vs-emittedpayment_total, donation-entry merge) before it is ever wired into an accept path. Follow-up flagged, deliberately NOT bundled into this slice.build_share's mandatory self-check proves serialization self-consistency (the committed hash_link/ref_hash/merkle folds reproduce exactly what a receiving verifier recomputes from the share bytes); it structurally cannot prove amount-level oracle parity, since no in-tree accept-path component reconstructs the coinbase from chain state. Amount parity is carried by the KAT layer + the oracle replay of the PROVISIONAL list. Stated explicitly in the header.ShareTracker::compute_share_targetcarries a v36 emergency time-decay ABSENT from the p2pool-dash oracle; using it to mint would fork us off under slow-pool conditions. The mint path must usedash::producer::compute_share_target(slices 2-3 must bind stratumshare_bitsto it too).packed_paymentsunconditionally. Producer implements the oracle rule; divergence only bites on invalid/empty payees in GBT.1+ishare_count offset and first-seen dedup across the 100-share walk (share_count < 110receive-side assert bounds it).Runtime wiring
Nothing is wired into
--run, stratum, or the node legs.c2pool-dash --selftestpasses; all 57 dash test binaries pass locally (Release,-j4); the change set is dash-fenced (no shared/core files touched).Base: branched from
fix/dash-stratum-work-servingbecause #733 is not yet merged; the PR is opened against that branch so the diff shows only slice-1 commits — please retarget to master after #733 lands.