diff --git a/src/net_processing.h b/src/net_processing.h --- a/src/net_processing.h +++ b/src/net_processing.h @@ -25,6 +25,14 @@ * reconstruction. */ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; +/** + * Headers download timeout expressed in microseconds. + * Timeout = base + per_header * (expected number of headers) + */ +// 15 minutes +static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; +// 1ms/header +static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -175,6 +175,8 @@ int nUnconnectingHeaders; //! Whether we've started headers synchronization with this peer. bool fSyncStarted; + //! When to potentially disconnect peer for stalling headers download + int64_t nHeadersSyncTimeout; //! Since when we're stalling block download progress (in microseconds), or //! 0. int64_t nStallingSince; @@ -217,6 +219,7 @@ pindexBestHeaderSent = nullptr; nUnconnectingHeaders = 0; fSyncStarted = false; + nHeadersSyncTimeout = 0; nStallingSince = 0; nDownloadingSince = 0; nBlocksInFlight = 0; @@ -3280,6 +3283,11 @@ pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; + state.nHeadersSyncTimeout = + GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * + (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / + (consensusParams.nPowTargetSpacing); nSyncStarted++; const CBlockIndex *pindexStart = pindexBestHeader; /** @@ -3662,6 +3670,45 @@ } } + // Check for headers sync timeouts + if (state.fSyncStarted && + state.nHeadersSyncTimeout < std::numeric_limits::max()) { + // Detect whether this is a stalling initial-headers-sync peer + if (pindexBestHeader->GetBlockTime() <= + GetAdjustedTime() - 24 * 60 * 60) { + if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && + (nPreferredDownload - state.fPreferredDownload >= 1)) { + // Disconnect a (non-whitelisted) peer if it is our only sync + // peer, and we have others we could be using instead. + // Note: If all our peers are inbound, then we won't disconnect + // our sync peer for stalling; we have bigger problems if we + // can't get any outbound peers. + if (!pto->fWhitelisted) { + LogPrintf("Timeout downloading headers from peer=%d, " + "disconnecting\n", + pto->GetId()); + pto->fDisconnect = true; + return true; + } else { + LogPrintf("Timeout downloading headers from whitelisted " + "peer=%d, not disconnecting\n", + pto->GetId()); + // Reset the headers sync state so that we have a chance to + // try downloading from a different peer. + // Note: this will also result in at least one more + // getheaders message to be sent to this peer (eventually). + state.fSyncStarted = false; + nSyncStarted--; + state.nHeadersSyncTimeout = 0; + } + } + } else { + // After we've caught up once, reset the timeout so we can't trigger + // disconnect later. + state.nHeadersSyncTimeout = std::numeric_limits::max(); + } + } + // // Message: getdata (blocks) //