Tell solo miners the truth about how they get paid - #42
Merged
rsantacroce merged 1 commit intoAug 22, 2026
Merged
Conversation
The "about the numbers" card stated the pps-classic story unconditionally. On a solo pool every substantive claim in it was wrong, and one of them was wrong in a way that costs a miner an afternoon: it told them to authorize with a Thunder address. stratum.c branches on pps_enabled at authorize, so in solo mode that is rejected — "invalid payout address in stratum username" — and the card then went on to explain the opposite error message. The card is now a partial with three bodies, branching on pool_mode. Solo says what solo does: the coinbase pays the finder directly, nothing accrues between blocks, a share that is not a block is worth nothing. It names the operator fee and where it goes, and adds two things nobody had written down — coinbase outputs mature after 100 confirmations, and the address must be P2WPKH, P2PKH or P2SH, because coinbase_address_to_script rejects taproot. Both are questions the old card left a solo miner to discover by hitting them. PPS-classic keeps the deposit and payout-worker explanation, which was always correct, and replaces the rate sentence. "Currently 1 000 sats × share difficulty" was hardcoded HTML describing a rate that is derived per template as (block value / net difficulty) * (1 - fee) and moves with difficulty; the literal had never been checked against the ledger it purported to describe. It now reads the live rate, the gross, and the fee from pool_meta, and says outright that the rate is not fixed. A pinned rate gets called out with the fee it actually implies, which is the failure the rate columns exist to expose. Everything else that was a literal is now live too: fee, operator address, pool wallet, sidechain id. Address examples follow the pool's network, so a signet pool shows tb1q rather than bc1q — misleading a miner about the network is the same class of error this whole card was making. An unknown mode names both and commits to neither. The two differ on what a share is worth and on what the username must be, so a guess there is not a harmless default.
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.
Stacked on #41 — it reuses the
res.locals.poolwiring from that PR. Base is2026-08-22-pool-identity-banner; retarget tomainonce #41 merges.The problem
The "about the numbers" card on
/stated the pps-classic storyunconditionally. On a solo pool every substantive claim in it was wrong:
pool_modedecides.pps_credits"pps_creditsrows are ever written.username: <your-Thunder-address>That last row is the one that costs real time.
src/stratum.c:849branches onpps_enabled: pps-classic requires a Thunder address, solo requires a Bitcoinaddress. So the card told solo miners to do the one thing that cannot work,
then explained the opposite error message — they'd hit "invalid payout
address in stratum username" while reading a paragraph about "invalid
thunder address".
And on both modes the rate was a lie of a different kind:
1 000 sats × share difficultywas hardcoded HTML. The real rate is derived per template as(block value ÷ net difficulty) × (1 − fee)and moves with difficulty. Thatliteral had never been checked against the ledger it purported to describe.
What it says now
views/partial/about-numbers.ejs, three bodies, branching onpool_mode.solo
Two things there were nowhere in the docs before: coinbase maturity, and
that taproot is rejected (
coinbase_address_to_scripthandles v0 witnessprograms, P2PKH and P2SH only). Both were previously discovered by hitting
them.
pps-classic
Keeps the deposit and payout-worker paragraphs — those were always correct —
and replaces the rate sentence:
A pinned rate (
rate_source = override) now gets called out with the fee itactually implies and a link to
/health— the exact failure the rate columnswere added to expose. Before the first template it says the rate hasn't been
published yet rather than rendering
0.00 sats.unknown mode
Names both and commits to neither. The modes differ on what a share is worth
and on what the username must be, so a guess is not a harmless default.
Everything is live now
Rate, gross, fee, operator address, pool wallet, sidechain id (
THUNDER_SIDECHAIN_ID,not a hardcoded
#9), stratum URL — all frompool_metavia theres.locals.poolfrom #41. Address examples follow the pool's network, so asignet pool shows
tb1q…; an ambiguous network (test/signet/regtest, whichis what a base58 operator address yields) falls back to prose rather than
inventing a prefix.
Testing
10 new tests in
dashboard/test/about-numbers.test.js, 94 total in the suite(92 pass / 2 pre-existing
/procskips). The ones worth naming:your-Thunder-address; pps-classic never emitsyour-bitcoin-address1 000 satsliteral is asserted absentbc1q…index.ejsrender against realstats.*output, so the partial can't break the page it lives onAlso rendered by hand against a live dashboard in both modes; the transcripts
are what's quoted above.
Note
The solo and pps cards both name addresses that PR #41's identity strip also
shows a few lines up. Kept deliberately — the strip is reference, the card is
explanation, and "the fee goes here" reads better inline. Easy to drop if
you'd rather not have the repetition.