Integrate JDP with PPLNS multi-output coinbaseOutputs#115
Open
warioishere wants to merge 5 commits into
Open
Conversation
|
@warioishere can you elaborate on your motivations for multi-output coinbase? the Sv2 JDP spec does allow JDS to establish multiple outputs via but unfortunately, Sv2 JDP spec does not allow non-custodial pooled payouts via multi-output (ala Ocean style); only the first output is standardized as the pool payout output |
|
we want to eventually enable non-custodial pooled payouts, but a Sv2 protocol extension will be required for that edit: oh sorry, just realized there's some extra context here: diving into it now, will report back (either there or here) later today |
|
cross referencing: stratum-mining/sv2-apps#388 (comment) |
32aa440 to
7ae94d3
Compare
c6d49fc to
09786a1
Compare
warioishere
added a commit
that referenced
this pull request
Apr 22, 2026
All of the following features were originally stacked as separate PRs on top of PPLNS: #119 feature/group-solo-mining group-solo engine + API #120 feature/mining-mode-endpoint GET /pplns/mode/:address #121 feature/block-template-mode-aware mode-aware block-template #122 feature/group-chart-endpoint chart + all security hardening + regtests Landing them as separate PRs would have exposed master to intermediate vulnerable states (selfLeave DoS, silent-add token leak, pending-out- of-coinbase math bug, etc.) until #122 closed the stack. All security and regtest work lived exclusively on #122, so merging the lower PRs first would ship a group-solo surface without its hardening. Rolling everything into #118 means the whole feature lands atomically on master with its full test + security story. PR #115 (JDP integration) stays separate, still upstream-blocked. # Conflicts: # src/controllers/pplns/pplns.controller.spec.ts # src/controllers/pplns/pplns.controller.ts
6 tasks
09786a1 to
eebcd58
Compare
Adds optional multi-output JDP coinbase via the spec-track extension 0x0003 — PPLNS and Group-Solo distributions can now be delivered through the Job Declaration Protocol without custody hand-off. - Base JDP behaviour stays exactly spec-pure: when ext 0x0003 isn't negotiated, AllocateMiningJobToken.Success carries one TxOut paying the miner directly (§6.4.3, single-output rule). - New ext 0x0001 (RequestExtensions) handler — the JDC opts into 0x0003 right after SetupConnection.Success, server responds with RequestExtensions.Success / .Error. - When 0x0003 is negotiated, JDS appends the coinbase_tx_output_weights TLV (Type=0x0003/0x01) to AllocateMiningJobToken.Success. Per-output weights are the per-address sats from PplnsService.getPayoutDistribution or GroupSoloService.getPayoutDistribution. - Group-Solo finder-bonus included via JDP: the miner allocating the token is passed as finderAddress, so an additional bonus output is emitted in the coinbase and reflected in the weights. - validateCoinbaseOutputs now checks all pool output scripts are present in coinbaseTxSuffix, not just the first. - TLV header is encoded big-endian per §3.4.3 (value internals stay LE). Spec compliance: §6.4.3 single-output rule preserved by default; ext 0x0003 §1.3 negotiation gate enforced; ext 0x0001 §4.1 error semantics (.Error when none requested are supported) implemented. Tests: 39 new + 875 existing pass; TLV byte sequence verified against the wire example in extensions/0x0003-coinbase-output-weights.md.
PushSolution field order was wrong — spec table §6.4.9 has nonce before ntime, but the SRI Rust reference (push_solution.rs) has ntime before nonce, and that's what real JDCs put on the wire. We follow SRI for interop; the spec table is a documentation bug. Pin the wire layout in a byte-level test so the regression can't sneak back in. Other spec-compliance fixes: - handleRequestExtensions now ignores stray pre-SetupConnection RequestExtensions frames (ext 0x0001 §4.2 — MUST arrive after SetupConnection.Success). - SV2 mining server now returns .Error (not empty .Success) when the JDC requests extensions none of which are supported (ext 0x0001 §4.1 — server MUST respond .Error in that case). - Consolidated the duplicate RequestExtensions definitions in sv2-messages.ts onto sv2-extensions-messages.ts (single source of truth, now incl. serializeRequestExtensionsError). - Documented PushSolution propagation per §6.1 + §6.4.9 (JDS MUST attempt to reconstruct/propagate — independent from JDC's own SubmitSolution path, by design, for orphan-risk reduction). New tests: - byte-level pin for PushSolution field order - spec wire-example roundtrip for the 0x0003 weights TLV - post-base TLV parsing roundtrip (AllocateMiningJobToken.Success + 0x0003 TLV) - JobDeclarationClient end-to-end state-machine tests through real handleFrame dispatch (negotiation success/error/mix, TLV emission gated by ext 0x0003 + multi-output payout, pre-setup rejection) 891 jest tests / 81 suites green; tsc clean.
Closes the per-share worker-attribution gap: a Stratum-V2 proxy that multiplexes multiple workers over a single extended channel can now attribute each share to the right worker, instead of every share landing on the channel-open user_identity. - Wire codec for the 0x0002 TLV (encodeWorkerIdTlv / parseWorkerIdTlv) in sv2-extensions-messages.ts. TLV header big-endian per §3.4.3, value is UTF-8 (max 32 bytes per spec §1.1). - New pure helper resolveShareWorkerNameFromTlv encodes the routing policy (extension-gated, bare-worker OR address.worker form, cross-account attribution dropped silently). - StratumV2Client adds 0x0002 to its SUPPORTED_MINING_EXTENSIONS and tracks negotiatedExtensions per-connection. handleSubmitSharesExtended derives the share's effective worker name from the trailing TLV and threads it into handleValidShare for shareTotalsCacheService and clientDifficultyStatisticsService (the two per-worker aggregations). - Security boundary: address part of the TLV is matched against the channel-locked address (case-insensitive). A TLV pointing at a different address is silently dropped — a multiplexing proxy MUST stay within the address it opened the channel under. 10 new resolver tests covering: extension-gated bypass, missing/ malformed TLV fallback, bare-worker / address.worker forms, cross- account drop, case-insensitive matching, nested dots, empty worker trailing-dot edge case.
939fe05 to
3c47a4c
Compare
Discovered in production 2026-05-11. Same class of doc-bug as PushSolution field order: spec table says channel_msg=0 but every real impl (SRI, bosminer) requires channel_msg=1. Fixed on master in 16434bc. Bitaxe firmware is permissive and works with both bit values.
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.
Summary
Removes solo-mining hack from JDP and integrates with PPLNS distribution.
Upstream blocker
JDC (SRI reference implementation) currently handles only a single coinbaseOutput. Multi-output support needs to be proposed to the SV2/SRI team. The pool side is ready.
Test plan