Skip to content

Commit 04de91a

Browse files
authored
Merge pull request #204 from frstrtr/bch/m5-blockdl-evicted-arrival
bch(M5): drop evicted-then-arrived block from the download queue
2 parents 517b9c4 + df8d578 commit 04de91a

2 files changed

Lines changed: 45 additions & 5 deletions

File tree

src/impl/bch/coin/block_download.hpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@
2727
// re-requested, so an overlapping getheaders locator batch or a re-announce
2828
// cannot double-download.
2929
//
30-
// NOT IN THIS SLICE (deferred per integrator 2026-06-18): in-flight timeout /
31-
// eviction (a block requested but never delivered by a stalling peer). That is
32-
// robustness hardening worth doing only once blocks are actually flowing; this
33-
// slice builds the happy-path window first.
30+
// IN-FLIGHT TIMEOUT / EVICTION (expire()): a block getdata'd but never delivered
31+
// by a stalling peer is timed out, requeued oldest-first, and re-issued; an
32+
// evicted block the peer later delivers anyway is flagged via false_evict_count
33+
// and dropped from the queue so it is not re-downloaded. NodeP2P drives this on
34+
// a steady-clock timer (see p2p_node.hpp BLOCK_DL_TIMEOUT_SEC).
3435
//
3536
// p2pool-merged-v36 SURFACE: NONE -- pure SPV/IBD wire-sync plumbing; no PoW
3637
// hash, share format, coinbase commitment, AuxPoW, or PPLNS math. PER-COIN
@@ -109,7 +110,18 @@ class BlockDownloadWindow {
109110
// On a healthy peer with a generous timeout NOTHING expires, so this
110111
// stays 0 -- a non-zero value means the BLOCK_DL_TIMEOUT_SEC fired on a
111112
// request the peer was merely slow to answer, not genuinely stalled.
112-
if (m_evicted.erase(h)) ++m_false_evict_count;
113+
if (m_evicted.erase(h)) {
114+
++m_false_evict_count;
115+
// expire() requeued this hash to the FRONT when it timed out; the
116+
// (merely slow) peer has now delivered it, so drop it from the pending
117+
// queue -- otherwise the next drain would re-getdata a block we already
118+
// hold. Linear scan is fine: eviction is rare (a healthy sync keeps
119+
// false_evict at 0). If it was already re-issued it is in m_in_flight,
120+
// not m_queue, and the scan simply finds nothing.
121+
for (auto qit = m_queue.begin(); qit != m_queue.end(); ++qit) {
122+
if (*qit == h) { m_queue.erase(qit); break; }
123+
}
124+
}
113125
auto it = m_in_flight.find(h);
114126
if (it == m_in_flight.end()) {
115127
// Unsolicited or already handled: still remember it so a later

src/impl/bch/test/block_download_test.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,34 @@ int main()
201201
assert(w.reissue_count() == 1);
202202
}
203203

204+
// ---- evicted-then-arrived: false_evict accounting + NO re-download ------
205+
// A block we expired() but the (merely slow) peer delivers anyway is a
206+
// PREMATURE eviction. The window must (a) flag it via false_evict_count
207+
// -- distinct from a clean arrival -- and (b) drop it from the pending
208+
// queue so the next drain does NOT re-getdata a block we already hold.
209+
{
210+
BlockDownloadWindow w(2);
211+
w.enqueue(hashes(0, 2)); // [H0 H1]
212+
w.next_requests(/*now=*/100); // H0,H1 issued @100
213+
assert(w.on_block_received(H(1)) == true); // H1 arrives clean
214+
assert(w.false_evict_count() == 0); // a clean arrival is NOT premature
215+
216+
// H0 stalls past the timeout -> evicted + requeued to the front.
217+
auto evicted = w.expire(/*now=*/200, /*timeout=*/50);
218+
assert(evicted.size() == 1 && evicted[0] == H(0));
219+
assert(w.reissue_count() == 1);
220+
assert(w.queued() == 1 && w.in_flight() == 0);
221+
222+
// The peer was merely slow: H0 arrives AFTER its eviction.
223+
assert(w.on_block_received(H(0)) == false); // unsolicited (no longer in flight)
224+
assert(w.false_evict_count() == 1); // flagged as a premature eviction
225+
226+
// ...and it must NOT be re-requested -- we already hold the body.
227+
assert(w.queued() == 0);
228+
assert(w.next_requests(/*now=*/300).empty());
229+
assert(w.idle());
230+
}
231+
204232
std::cout << "bch block_download window: ALL PASS\n";
205233
return 0;
206234
}

0 commit comments

Comments
 (0)