From 73e77c3bc259a4c8e0a4cc5c376f1a9545439bdd Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 08:29:44 +0000 Subject: net: retry block fetches every 15s, not every two minutes AskFor keys its retry spacing off mapAlreadyAskedFor, which is global to the inv, so the two-minute gap does more than pace re-requests to one peer: it strings the getdatas for one block across its announcers two minutes apart. When the first peer asked does not serve the body, the second candidate is not asked for 120s and the ninth for 960s -- the observed 16-minute tip stall of #33, during which the pool kept broadcasting jobs against the stale tip and 5 of 12 found blocks orphaned. A block body is at most 1 MB and somebody demonstrably has it -- they announced it -- so on this timescale "not here yet" means "not coming from that peer". Blocks now retry on BLOCK_FETCH_RETRY_SECS (15s); transactions keep the two-minute pacing, which for them is the point. --- src/net.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/net.h b/src/net.h index 9f3c7e4..2e4d8cb 100644 --- a/src/net.h +++ b/src/net.h @@ -89,6 +89,15 @@ static const int BTF_RECV_TIMEOUT_SECS = 30 * 60; static const int BTF_SEND_STALL_SECS = 10 * 60; static const int BTF_HANDSHAKE_GRACE_SECS = 60; +// How long a requested block gets before it is asked for again, in seconds. +// Used twice: CNode::AskFor spaces the queued per-announcer block retries this +// far apart instead of the two minutes transactions get, and the failover +// sweep in main.cpp re-issues a getdata to a different peer once a sent +// request has gone unanswered this long. A block body is at most 1 MB and +// somebody demonstrably has it -- they announced it -- so on this timescale +// "not here yet" means "not coming from that peer", not "still in transit". +static const int BLOCK_FETCH_RETRY_SECS = 15; + // A serialized object cannot be larger than MAX_SIZE, so no valid network // message needs the old 256 MB header allowance. Keep the wire cap aligned with // what the deserializer can accept. How long a peer may sit on a partial @@ -795,8 +804,16 @@ public: static int64 nLastTime; nLastTime = nNow = max(nNow, ++nLastTime); - // Each retry is 2 minutes after the last - nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow); + // Each retry is 2 minutes after the last -- and because nRequestTime + // is shared across peers, the same spacing is what strings requests + // for one inv across its announcers. For transactions that is pacing; + // for a new block it was the stall of issue #33: nine peers announce + // the block, the first one asked does not serve it, and the ninth + // candidate is not even asked until sixteen minutes in, with the pool + // mining on the stale tip the whole time. Blocks retry on the fetch + // timescale instead. + int64 nRetryUsecs = (inv.type == MSG_BLOCK ? BLOCK_FETCH_RETRY_SECS : 2 * 60) * 1000000LL; + nRequestTime = max(nRequestTime + nRetryUsecs, nNow); mapAskFor.insert(make_pair(nRequestTime, inv)); } -- cgit v1.2.3