Skip to content

Integrate JDP with PPLNS multi-output coinbaseOutputs#115

Open
warioishere wants to merge 5 commits into
blitzpool-masterfrom
feature/pplns-jdp-integration
Open

Integrate JDP with PPLNS multi-output coinbaseOutputs#115
warioishere wants to merge 5 commits into
blitzpool-masterfrom
feature/pplns-jdp-integration

Conversation

@warioishere
Copy link
Copy Markdown
Owner

@warioishere warioishere commented Mar 29, 2026

Summary

Removes solo-mining hack from JDP and integrates with PPLNS distribution.

  • getCoinbaseOutputsForToken(): Returns PPLNS distribution as multi-output Vec (all 0 sats per spec 6.4.3). Falls back to pool fee when PPLNS disabled.
  • validateCoinbaseOutputs(): Validates ALL pool output scripts present in declared coinbase, not just first.
  • user_identifier: Used for identification/logging only, not coinbase construction.

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

  • 56 tests passing (34 PPLNS + 22 JDP)
  • getCoinbaseOutputsForToken: returns pool fee fallback when PPLNS disabled
  • getCoinbaseOutputsForToken: returns empty when no addresses configured
  • validateCoinbaseOutputs: validates all output scripts (multi-output)
  • Compilation clean

@plebhash
Copy link
Copy Markdown

plebhash commented Apr 2, 2026

@warioishere can you elaborate on your motivations for multi-output coinbase?

the Sv2 JDP spec does allow JDS to establish multiple outputs via AllocateMiningJobToken.Success, and SRI JDC is able to accomodate for that

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

@plebhash
Copy link
Copy Markdown

plebhash commented Apr 2, 2026

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:

stratum-mining/sv2-apps#388

diving into it now, will report back (either there or here) later today

@plebhash
Copy link
Copy Markdown

plebhash commented Apr 2, 2026

cross referencing: stratum-mining/sv2-apps#388 (comment)

@warioishere warioishere force-pushed the feature/pplns-pool-support branch from 32aa440 to 7ae94d3 Compare April 19, 2026 11:57
@warioishere warioishere force-pushed the feature/pplns-jdp-integration branch from c6d49fc to 09786a1 Compare April 19, 2026 11:57
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
@warioishere warioishere mentioned this pull request Apr 22, 2026
6 tasks
@warioishere warioishere force-pushed the feature/pplns-jdp-integration branch from 09786a1 to eebcd58 Compare May 11, 2026 11:58
@warioishere warioishere changed the base branch from feature/pplns-pool-support to blitzpool-master May 11, 2026 12:42
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.
@warioishere warioishere force-pushed the feature/pplns-jdp-integration branch from 939fe05 to 3c47a4c Compare May 11, 2026 13:12
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.
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.

2 participants