diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-11 13:10:57 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 04:37:36 +0000 |
| commit | 090628b34d1aefc7029be37d7bc8512b5edb3ca5 (patch) | |
| tree | 65922fff3bac67bb07fbba4f5194b9af0e160250 /src/rpc.cpp | |
| parent | 78bdfe962a00edc1cdf69c4fb565e7f273f337a3 (diff) | |
pool: gate jobs on peer height, not tip age
The sync gate compared the tip timestamp against 30 minutes. That cannot
distinguish "we are not synced" from "the network is slow", and it failed
in both directions on the same day.
A pool mining its own fork keeps its tip fresh, so the check passed for
the whole of heights 3966-4085 -- 120 consecutive blocks, all orphaned,
5890.50 BTF of payouts voided, and nothing in the log to say so. Then,
once the pool stopped finding blocks, the same check refused to issue
jobs against a tip that was the network's own best. The pool is ~90% of
the hashrate, so nobody else could move the chain either: stale tip, no
jobs; no jobs, stale tip.
Gate on the height peers announce in their version message instead.
Median, so one peer cannot stall us with an absurd claim, and a block of
slack to match WarnIfBehind. Peers predating the announcement report -1
and are ignored; with no reports at all we mine our best chain, which is
what the node does anyway without a pool attached.
(cherry picked from commit 4e76e34f37a82c0885f75db7dfb9e49db410171d)
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index ae94289..8a0d112 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -143,17 +143,40 @@ static bool RebuildJob() | |||
| 143 | return false; | 143 | return false; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | // Don't issue jobs until the chain is synced: mining against a stale | 146 | // Don't issue jobs while we are behind the network: mining against a |
| 147 | // tip produces blocks the network will orphan, and payout entries | 147 | // stale tip produces blocks the network will orphan, and payout |
| 148 | // that can never be paid (their coinbases never exist on the main | 148 | // entries that can never be paid (their coinbases never exist on the |
| 149 | // chain). Require at least one peer and a fresh tip. | 149 | // main chain). Require at least one peer, and require that no peer |
| 150 | // claims a longer chain than ours. | ||
| 150 | if (vNodes.empty()) { | 151 | if (vNodes.empty()) { |
| 151 | LogPrint("pool", "RebuildJob: no peers yet -- not issuing jobs\n"); | 152 | LogPrint("pool", "RebuildJob: no peers yet -- not issuing jobs\n"); |
| 152 | return false; | 153 | return false; |
| 153 | } | 154 | } |
| 154 | if (GetAdjustedTime() - (int64)pindexBest->nTime > 30*60) { | 155 | // This used to compare the tip's timestamp against 30 minutes, which |
| 155 | LogPrint("pool", "RebuildJob: tip is %llds old -- still syncing, not issuing jobs\n", | 156 | // was wrong in both directions and cost real money. |
| 156 | (long long)(GetAdjustedTime() - (int64)pindexBest->nTime)); | 157 | // |
| 158 | // It cannot tell "we are not synced" from "the network is slow". A | ||
| 159 | // pool mining its own fork keeps its tip timestamp fresh forever, so | ||
| 160 | // the check passed for the whole of heights 3966-4085 -- 120 | ||
| 161 | // consecutive blocks, every one of them orphaned, 5890.50 BTF of | ||
| 162 | // payouts voided. Then, once the pool stopped finding blocks, the | ||
| 163 | // same check refused to issue jobs against a tip that was perfectly | ||
| 164 | // valid and was the network's own best, and the pool -- roughly 90% | ||
| 165 | // of the hashrate -- could not restart the chain it was the majority | ||
| 166 | // of. Stale tip, so no jobs; no jobs, so the tip stays stale. | ||
| 167 | // | ||
| 168 | // The height peers announce in their version message answers the | ||
| 169 | // question the age check was only guessing at. Median, so one peer | ||
| 170 | // claiming an absurd height cannot stall us, and one block of slack | ||
| 171 | // to match WarnIfBehind, because somebody is always mid-relay. A | ||
| 172 | // peer that predates the announcement reports -1 and is ignored; if | ||
| 173 | // nobody has told us anything we mine our best chain, which is what | ||
| 174 | // the node would do without a pool attached. | ||
| 175 | int nPeerHeight = GetPeerMedianHeight(); | ||
| 176 | if (nPeerHeight >= 0 && nBestHeight < nPeerHeight - 1) { | ||
| 177 | LogPrint("pool", "RebuildJob: %d blocks behind the network " | ||
| 178 | "(height %d, peers report %d) -- not issuing jobs\n", | ||
| 179 | nPeerHeight - nBestHeight, nBestHeight, nPeerHeight); | ||
| 157 | return false; | 180 | return false; |
| 158 | } | 181 | } |
| 159 | 182 | ||