diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-11 20:52:09 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-19 22:48:27 +0000 |
| commit | e236d013e973fd00cbdbed4bea1e5f373c0d1236 (patch) | |
| tree | b184641daad4310cdef51dff8bece2743af29f8a /src/main.cpp | |
| parent | db2692dcd8984fe0e5a2a6a796f7b3cbee216c32 (diff) | |
pool: refuse jobs when no peer has reported a height for 20 minutes
The peer-height gate replaced a tip-age check that was wrong in both
directions. It is a strict improvement and it is still blind in the one
case that matters most.
When the node goes deaf -- peers still in vNodes, nothing arriving -- no
peer announces anything, GetPeerMedianHeight() returns -1, and the
fallback mines our best chain. That fallback is right for a node that has
not been told yet and wrong for a node that has stopped being able to
hear, and a height nobody sent cannot tell those apart. A wedged node is
exactly a node with no peer height information, so the gate was silent
for the whole of heights 3966-4085: 120 blocks, 5890.50 BTF voided.
Time can tell them apart. GetPeerHeightAgeSecs() measures how long since
any peer reported a height, counting from first use rather than the epoch
so a starting node does not refuse jobs before its first handshake. Past
20 minutes the pool stops issuing jobs and says so unconditionally in the
log. /poolmineblind overrides it: without an escape hatch a genuinely
isolated pool that is 90% of the hashrate could never restart its own
chain, which is the deadlock the tip-age check used to cause.
Also stop nStartingHeight from going stale. It was a snapshot taken once
during the version handshake and never touched again, so on a long-lived
connection the median is built from heights the peers had hours ago, and
the behind-the-network check drifts toward 'everyone is behind us' and
quietly stops firing. A peer that hands us a block is demonstrably at
least at that block's height.
(cherry picked from commit 0800c18faf703b4acde1b131d0b742d37a86d92b)
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp index 382a008..5d0b70a 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -88,6 +88,7 @@ string strSetDataDir; | |||
| 88 | int nDropMessagesTest = 0; | 88 | int nDropMessagesTest = 0; |
| 89 | bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local test) | 89 | bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local test) |
| 90 | bool fPoolNoAnnounce = false; // /noannounce: operator serves miners but never publishes pool announcements | 90 | bool fPoolNoAnnounce = false; // /noannounce: operator serves miners but never publishes pool announcements |
| 91 | bool fPoolMineBlind = false; // /poolmineblind: issue jobs even when no peer has reported a height | ||
| 91 | 92 | ||
| 92 | // Settings | 93 | // Settings |
| 93 | int fGenerateBitcoins; | 94 | int fGenerateBitcoins; |
| @@ -3172,7 +3173,21 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | |||
| 3172 | pfrom->AddInventoryKnown(inv); | 3173 | pfrom->AddInventoryKnown(inv); |
| 3173 | 3174 | ||
| 3174 | if (ProcessBlock(pfrom, pblock.release())) | 3175 | if (ProcessBlock(pfrom, pblock.release())) |
| 3176 | { | ||
| 3175 | mapAlreadyAskedFor.erase(inv); | 3177 | mapAlreadyAskedFor.erase(inv); |
| 3178 | |||
| 3179 | // A peer that just handed us a block is demonstrably at least at | ||
| 3180 | // that block's height. Without this, nStartingHeight is a snapshot | ||
| 3181 | // taken once during the version handshake and never touched again, | ||
| 3182 | // so on a long-lived connection it is the peer's height from | ||
| 3183 | // however many hours ago it happened to connect. The pool's | ||
| 3184 | // behind-the-network check compares against the median of those | ||
| 3185 | // numbers, so as connections age it drifts toward "everyone is | ||
| 3186 | // behind us" and quietly stops firing -- the opposite of its job. | ||
| 3187 | map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); | ||
| 3188 | if (mi != mapBlockIndex.end() && mi->second) | ||
| 3189 | pfrom->nStartingHeight = max(pfrom->nStartingHeight, mi->second->nHeight); | ||
| 3190 | } | ||
| 3176 | } | 3191 | } |
| 3177 | 3192 | ||
| 3178 | 3193 | ||