upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-11 13:10:57 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:26 +0000
commitdb2692dcd8984fe0e5a2a6a796f7b3cbee216c32 (patch)
tree45e00acf1557d6d1604aed08dd670766679b8b98 /src/rpc.cpp
parentec1627c68d3b6abdbf84b2a816d98bdd4ce113c9 (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.cpp37
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